BCTF 2015 pwn challenge: zhongguancun
The general idea to pwn this toy online store system is as following, firstly we need to find a control flow hijack vulnerability which in this case is a vtable hijack based on heap buffer overflow, secondly we will bypass all the integrity checks in the program and the memory defenses(e.g. ASLR) to call “system(“sh”)” in libc.
After reading the Exploit code of Idolf, I finally understand the exploit steps:
- Triggering a heap overflow to overwrite the virtual table of an object by inputing longest string allowed.
- Force the program to call the function to “calculate wholesale price” (but actually become an arbitrary memory write) and overwrite an integer pointer to point to the PLT of function “sprintf()”.
- Force the program to overwrite the integer pointer which actually change the .plt of “sprintf()” to “system()”
A Heap overflow can be triggered by the following code:
from pwn import *
#r = remote('22.214.171.124',6666)
r = process('/root/wrapper.sh')
#eip = p32(0x08049b74)
eip = p32(0xDEADBEEF)
name = cyclic(63,alphabet = "DEFGHI", n = 4)
print 'store name:'+name
phonename = cyclic(31,alphabet = "UVWXYZ", n = 4)
print 'phone name:'+ phonename
r.sendline('4') #blackberry OS
description = cyclic(74,alphabet = "ABCDEF", n = 4)+eip+'A'
for _ in range(15):
When we add a phone to the store, a memory region is allocated to store the phone’s information, all the input are safely handled, no buffer overflow vulnerability is found. We can add no more than 16 phones.
The program will allcocate an area to store the information of all the phones we added the first time we call ‘generate menu’ function.
But if we pad each phone’s information to its max length(63 for store name, 31 for phone name, 11 for price by using negative number and 79 for phone description), an overflow of 13 bytes will accur and overwrite the virtual table of the first phone object after the menu object . We can thus control the virtual table. However, a vtable check function is called before the virtual call. The virtual table must be in non-writable memory area! As a result I can not direct modify the vtable to the heap to execute shellcode.
If we modify the virtual table with a 4 bytes offset, from 0x08049B70(get_wholesale_price) to 0x08049B74(print_phone_information) , and then we try to get a wholesale price, the virtual call “get_whole_sale” becomes a call to “print_phone_information”, the phone information is printed to “pointer to cash”, and we can modify pointer to cash to point to one byte before the .plt table entry : sprintf() at 0x0804B00B . That’s right, we are going to overwrite plt entry of sprintf() to system()!
However it is still a challenge to bypass ASLR and locate the libc function system(). We know the relative offset is 0xEFE0 between sprintf() and system(). What we are going to do is to subtract this value from the original value: address of sprintf().
In short, we need to find a negative number consists of ten digits and a positive number under the constraint that their multiple equals -0xEFE0 . This is possible by exploiting an integer overflow.
(To be continued)