heap入门

堆利用Heap Exploitation

https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
https://ctf-wiki.org/pwn/linux/glibc-heap/introduction/

基础知识学习ptmalloc

命令::

gef➤ x/32gx &main_arena
gef➤ heap bins

申请与释放

malloc()
free()

malloc()

malloc() 使用brk() mmap()申请内存

brk() sbrk()

Theprogram breakis theaddressof the first location beyond the current end of the data region.

#include <unistd.h>

int brk(void* end_data_segment);
void *sbrk(intptr_t increment);


┌─[zentreisender@parrotos]─[~/Documents/heap]
└──╼ $./brk
Welcome to sbrk example:178107
Program Break Location1:0x555db6660000
a
Program break Location2:0x555db6661000
Program Break Location3:0x555db6660000
a

代码

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
/* sbrk and brk example */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
void *curr_brk, *tmp_brk = NULL;

printf("Welcome to sbrk example:%d\n", getpid());

/* sbrk(0) gives current program break location */
tmp_brk = curr_brk = sbrk(0);
printf("Program Break Location1:%p\n", curr_brk);
getchar();

/* brk(addr) increments/decrements program break location */
brk(curr_brk+4096);

curr_brk = sbrk(0);
printf("Program break Location2:%p\n", curr_brk);
getchar();

brk(tmp_brk);

curr_brk = sbrk(0);
printf("Program Break Location3:%p\n", curr_brk);
getchar();

return 0;
}

brk 参数是指针,设置break值
所以先用sbrk(0)获得当前break值的指向指针
再设置brk(sbrk(0)+4096)

mmap()

malloc 会使用 mmap来创建独立的匿名映射段。匿名映射的目的主要是可以申请以0填充的内存,并且这块内存仅被调用进程所使用。

addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

munmap()

char* addr
addr = (char*) malloc(1000);

虽然只是申请了1000个字节,但是我们却得到了0x0806c000-0x0804b000=0x21000个字节的堆,int(0x21000,10)/1024=132,原来这132KB的堆空间叫做arena,此时因为是主线程分配的,所以这个区域叫做 main arena,(称这一块连续的内存区域为 arena,)申请的内存会一直从这个 arena 中获取,直到空间不足,当 arena 空间不足时,它可以通过增加brk的方式来增加堆的空间。

this shows heap memory is created by increasing program break location ( ie) using brk syscall

free()

Allocated memory region (of size 1000 bytes) is released only to ‘glibc malloc’ library,‘glibc malloc’ doesnt get new heap memory from kernel, instead it will try to find a free block in bin. And only when no free block exists, it obtains memory from kernel.

1.清空此堆块的 user data
2.将此堆块的指针存储到 main_arena 中了(或是 fast bin 中)

看出实际真的分配给程序的内存为1M(b7500000-b7600000)。heap memory of size 1 MB ,而且,只有132KB的部分具有可读可写权限,这一块连续的区域成为thread arena。是使用mmap()分配的,而不是sbrk()
当用户请求的内存大于128KB时,并且没有任何arena有足够的空间时,那么系统就会执行mmap函数来分配相应的内存空间,而不是sbrk()。这与这个请求来自于主线程还是从线程无关。

Instead allocated memory region (of size 1000 bytes) is released to ‘glibc malloc’, which adds this freed block to its thread arenas bin.

chunk结构

gef➤  x/32gx $rax-0x10
0x555555559290:    0x0000000000000000    0x0000000000000021
0x5555555592a0:    0x0000000000000000    0x0000000000000000
0x5555555592b0:    0x0000000000000000    0x0000000000020d51
0x5555555592c0:    0x0000000000000000    0x0000000000000000
0x5555555592d0:    0x0000000000000000    0x0000000000000000
0x5555555592e0:    0x0000000000000000    0x0000000000000000
0x5555555592f0:    0x0000000000000000    0x0000000000000000
0x555555559300:    0x0000000000000000    0x0000000000000000
0x555555559310:    0x0000000000000000    0x0000000000000000
0x555555559320:    0x0000000000000000    0x0000000000000000

presize 8字节
size 8字节
0x5555555592a0 开始是user-data
64位最小分配16B,32位最小分配8B,所以是16+8+8+1=21
user-data+header-len+pre_(1)=21
size最低三位,LSB表示前一chunk是否分配。

top chunk

结尾于arena的最高地址处
堆地址从低地址向高地址增长
6Vae81.png

在程序在向堆管理器申请内存时,没有合适的内存空间可以分配给他,此时就会从 top chunk 上”剪切”一部分作为 chunk 分配给他

chunk()

allocated chunk

prev_size: If the previous chunk is free, this field contains the size of previous chunk. Else if previous chunk is allocated, this field contains previous chunk’s user data.
size: This field contains the size of this allocated chunk. Last 3 bits of this field contains flag information.

PREV_INUSE (P) – This bit is set when previous chunk is allocated.
IS_MMAPPED (M) – This bit is set when chunk is mmap’d.
NON_MAIN_ARENA (N) – This bit is set when this chunk belongs to a thread arena.

Other fields of malloc_chunk (like fd, bk) is NOT used for allocated chunk. Hence in place of these fields user data is stored.
·由于存储malloc_chunk以及对齐目的需要一些额外的空间,因此用户请求的大小将转换为可用大小(内部表示大小)。
·转换的方式是不会设置可用大小的最后3位,因此将其用于存储标志信息。
6VaHR1.png

free chunk

prev_size: No two free chunks can be adjacent together. When both the chunks are free, its gets combined into one single free chunk. Hence always previous chunk to this freed chunk would be allocated and therefore prev_size contains previous chunk’s user data.
fd: Forward pointer – Points to next chunk in the same bin (and NOT to the next chunk present in physical memory).
bk: Backward pointer – Points to previous chunk in the same bin (and NOT to the previous chunk present in physical memory).
6VaOsK.png

bins 垃圾桶

已经申请到的内存空间大小进行释放,来决定放入哪类 bins 当中去。
Fast bin
Unsorted bin
Small bin
Large bin

fast bin

64位下
Chunks of size 0x20 to 0x80 bytes is called a fast chunk.
其中的0x10字节是用来存prev_size和size

Number of bins – 10
Each fast bin contains a single linked list (a.k.a binlist) of free chunks.
Fast bins contain a binlist of chunks whose sizes are 8 bytes apart. ie) First fast bin (index 0) contains binlist of chunks of size 16 bytes, second fast bin (index 1) contains binlist of chunks of size 24 bytes and so on…

During malloc initialization, maximum fast bin size is set to 64 (!80) bytes.

small and large bins

When small or large chunk gets freed instead of adding them in to their respective bins, its gets added into unsorted bin. 重要
This approach gives ‘glibc malloc’ a second chance to reuse the recently freed chunks.
Chunk size – There is no size restriction, chunks of any size belongs to this bin.

fast bins

────────────────────── Fastbins for arena 0x7ffff7dd1b20 ──────────────────────
Fastbins[idx=0, size=0x10]  ←  Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)
Fastbins[idx=1, size=0x20]  ←  Chunk(addr=0x602050, size=0x30, flags=PREV_INUSE)
Fastbins[idx=2, size=0x30]  ←  Chunk(addr=0x602080, size=0x40, flags=PREV_INUSE)
Fastbins[idx=3, size=0x40]  ←  Chunk(addr=0x6020c0, size=0x50, flags=PREV_INUSE)
Fastbins[idx=4, size=0x50]  ←  Chunk(addr=0x602110, size=0x60, flags=PREV_INUSE)
Fastbins[idx=5, size=0x60]  ←  Chunk(addr=0x602170, size=0x70, flags=PREV_INUSE)
Fastbins[idx=6, size=0x70]  ←  Chunk(addr=0x6021e0, size=0x80, flags=PREV_INUSE)

a chunk of size 0x20-0x2f would fit into idx 0, a chunk of size 0x30-0x3f would fit into idx 1, and so on and so forth.

有七条链表,每个链表有一个头,后面接着free的块
链表后进先出,使用头插法

tcachebins

在glibc2.26中引入
一个链条同一时间只能有7个块
可以有64条链条
size任意0x20-0x410
当size不大(这个程度后面讲)堆块free后,不会直接进入各种bin,而是进入tcache,如果下次需要该大小内存,直接讲tcache分配出去
多出的块到达fastbins,(在大小符合的情况下)
https://www.freebuf.com/articles/system/234219.html
https://guyinatuxedo.github.io/25-heap/index.html

tcachebins就相当于fastbins 而且较少检查

Unsorted, Large and Small Bins

双向链表
头部都在同一数组中,有不同的索引值。
0x00: Not Used
0x01: Unsorted Bin
0x02 - 0x3f: Small Bin
0x40 - 0x7e: Large Bin

Unsorted Bin只有一条链, 62 for the Small Bin, and 63 for the Large Bin

The small bins on x64 consists of chunk sizes under 0x400 (1024 bytes), and on x86 consists of chunk sizesunder 0x200(512 bytes), and the large bin consists of values above those.

释放后,首先插入的是unsorted bin
since the unsorted bin chunk could not serve the requested size of 0x1000, it was sorted to its corresponding list of in the small bin at idx 4
当再次分配但不满足要求时,将进行排序 到相应的bin上
若满足要求,就分配
https://guyinatuxedo.github.io/25-heap/index.html

lagestbin 中还有fwd_nextsize and bk_nextsize

Consolidation 合并

相邻的空闲块都会合并

Top chunk

The Top Chunk is essentially a large heap chunk that holds currently unallocated data.
first time calling malloc reiterate the top chunk holds unallocated data that isn’t in the bin list.

malloc will try to allocate chunks from the bin lists before allocating them from the top chunk

We can see that two things have happened to the top chunk. Firstly that it moved down to 0x602120 from 0x602020 to make room for the new allocation from itself. Secondly, we see that it’s size was shrunk by 0x100, because of the 0x100 byte allocation from it.
低地址向高地址增长

depending on the version of malloc and if the chunk size is fast bin or tcache, this behavior doesn’t always show itself.
挨着top chunk,释放并不一定回到top chunk,旧的size值并不会清零,我的虚拟机上运行就变为tcachebins

gef➤  heap bins 
──────────────────────────────────────────────────────────────── Tcachebins for arena 0x7ffff7f9fb80 ────────────────────────────────────────────────────────────────
Tcachebins[idx=14, size=0x100] count=1  ←  Chunk(addr=0x5555555592c0, size=0x100, flags=PREV_INUSE) 

one thing you will see us do a lot of is allocated a small chunk in between our freed chunks and the top chunk, just to prevent that consolidation.
攻击时,要阻止释放的chunk回到top chunk,所以中间放一个小块分配内存。

main arena

contains the head pointers for the bin lists,
heap bins命令下可以显示

利用

基于这些bug:
leverage the bugs and a bit of heap grooming to edit a freed chunk in one of the bin lists. Then from being able to edit a freed chunk in one of the bin lists we can launch a bin attack

+--------------------+----------------------------+-----------------------+
|   Bug Used         |  Bin Attack                |   House               |
+--------------------+----------------------------+-----------------------+
|                    |  Fast Bin Attack           |   House of Spirit     |
|   Double Free      |  tcache attack             |   House of Lore       |
|   Heap Overflow    |  Unsorted Bin Attck        |   House of Force      |
|   Use After Free   |  Small / Large Bin Attck   |   House of Einherjar  |
|                    |  Unsafe Unlink             |   House of Orange     |
+--------------------+----------------------------+-----------------------+

两大利器gef 和 源码 malloc.c
When you attempt to use LD_PRELOAD to have a binary use a specific libc file, you might find an issue if the linker’s are not compatible.

fastbin 链表头插法

https://heap-exploitation.dhavalkapil.com/attacks/double_free
分配 也是从头部开始分配

double free 原理,两次释放,将同一块重复加入链表中,分配自然重复使用了