0%

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

然后,我们用IDA分析一下,发现是一个brain fuck语言的解释器,其中在>指令操作中,存在一个off by one,ptr指针可以指向string对象,

阅读全文 »

C++的vector的erase函数实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
iterator erase(iterator position){
if (position + 1 != end())
{
// 把从 position+1 到 finish 之间的元素一个一个复制到从 position 指向
//的空间,这样,就把 position 原来指向的元素个覆盖了

copy(position + 1, finish, position);
}

--finish;

destroy(finish);

return position;
}

就是将position+1 到 finish 之间的元素一个一个复制到从 position 指向的空间,这样,就把 position 原来指向的元素个覆盖了。同时注意到destroy(finish);释放最后一个元素,因此position位置的元素不会被释放,最后一个元素会被释放造成UAF。

阅读全文 »

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

然后,我们用IDA分析一下,add函数中,如果字符串长度大于15,则单独malloc一块内存存放字符串,否则直接存结点里。

阅读全文 »

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

然后,我们用IDA分析一下,edit函数里使用了abs函数,abs函数接收4字节有符号int数,当传入0x80000000时,其返回结果仍然是0x80000000,由于4字节int正数将无法表示这么大,因此,其值是一个负数,由此,可以造成堆溢出。

阅读全文 »

Tcache_Stashing_Unlink_Attack就是calloc的分配不从tcache bin里取chunk,calloc会遍历fastbin、small bin、large bin,如果在tcache bin里,对应的size的bin不为空,则会将这些bin的chunk采用头插法插入到tcache bin里。首先,我们来看一下glibc 2.29的源码。

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
/* 
If a small request, check regular bin. Since these "smallbins"
hold one size each, no searching within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/

if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin) //取该索引对应的small bin中最后一个chunk
{
bck = victim->bk; //获取倒数第二个chunk
if (__glibc_unlikely (bck->fd != victim)) //检查双向链表完整性
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck; //将victim从small bin的链表中卸下
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_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); //获取对应size的tcache索引
if (tcache && tc_idx < mp_.tcache_bins) //如果该索引在tcache bin范围
{
mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count //当tcache bin不为空并且没满,并且small bin不为空,则依次取最后一个chunk插入到tcache bin里
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck; //将当前chunk从small bin里卸下
bck->fd = bin;
//放入tcache bin里
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

如上,我们看到,从small bin中取出最后一个chunk的时候,对双向链表做了完整性的检查,然而,后面将剩余chunk放入tcache bin的时候却没有这个检查。然后,bck->fd = bin这句代码,可以将bck->fd处写一个main_arena地址。如果我们可以控制bck,那么就能实现任意地址处写一个main_arena的地址。同理,如果我们能够控制small bin的bck,并且保证vuln_addr->fd = bck,那么就能分配到vuln_addr处。

阅读全文 »

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

然后,我们用IDA分析一下,最多创建10个堆,并且size不能超过0x60

阅读全文 »