heap入门1

命令

"LD_PRELOAD":"./libc-2.23.so" 更改glibc版本
pwd显示当前目录
apropos搜索man手册
gef->search-patten
objdump -M intel汇编改为intel语法
set disassambly intel gdb更改语法
b print printf函数下断点

在gdb 里启动gef
(gdb) source ~/.gdbinit-gef.py

或者传递命令
gdb.attach(p,"source ~/.gdbinit-gef.py")


gef➤ heap-view

gef➤  got

GOT protection: Partial RelRO | GOT functions: 3

[0x804c00c] printf@GLIBC_2.0  →  0x8049036
[0x804c010] __libc_start_main@GLIBC_2.0  →  0xf7ddcd40
[0x804c014] __isoc99_scanf@GLIBC_2.7  →  0x8049056

gef➤  heap bins
gef➤  heap chunks

gef➤  heap chunks
Chunk(addr=0x555555559010, size=0x290, flags=PREV_INUSE)
    [0x0000555555559010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x5555555592a0, size=0x20, flags=PREV_INUSE)
    [0x00005555555592a0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x5555555592c0, size=0x20d50, flags=PREV_INUSE)  ←  top chunk
gef➤  x/6gx 0x5555555592c0
0x5555555592c0:    0x0000000000000000    0x0000000000000000
0x5555555592d0:    0x0000000000000000    0x0000000000000000
0x5555555592e0:    0x0000000000000000    0x0000000000000000
gef➤  x/6gx 0x5555555592c0-0x10
0x5555555592b0:    0x0000000000000000    0x0000000000020d51
0x5555555592c0:    0x0000000000000000    0x0000000000000000
0x5555555592d0:    0x0000000000000000    0x0000000000000000

查看glibc版本

┌─[✗]─[zentreisender@parrotos]─[~]
└──╼ $ldd --version
ldd ./program 显示链接器,加载器版本

基础

call malloc, it returns a pointer to a chunk
返回一个地址

64bit 一个chunk段16字节,32bit 8 字节

查看二进制文件依赖的动态链接库命令 ldd

PIE

PIE全称是position-independent executable,中文解释为地址无关可执行文件,该技术是一个针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的一个防护技术

RELRC

Firstly, PLT needs to be located at a fixed offset from the .text section. Secondly, since GOT contains data used by different parts of the program directly, it needs to be allocated at a known static address in memory. Lastly, and more importantly, because the GOT is lazily bound it needs to be writable.

PLT GOT 的偏移都是 写死在文件上
the linker resolves all dynamically linked functions at the beginning of the execution, and then makes the GOT read-only. 这就是RELRC

In partial RELRO, the non-PLT part of the GOT section (.got from readelf output) is read only but .got.plt is still writeable. Whereas in complete RELRO, the entire GOT (.got and .got.plt both) is marked as read-only.

.got Section 存放外部全局变量的 GOT 表,非延迟绑定
.got.plt Section 存放外部函数的 GOT 表,例如 printf,采用延迟绑定。

.got is for relocations regarding global ‘variables’ while .got.plt is a auxiliary section to act together with .plt when resolving procedures absolute addresses

编译安装glibc各版本

下载源码后
https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html

gcc10编译安装glibc 2.23

../glibc-2.20/configure --prefix=/usr --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin --disable-werror

--disable-werror 这项是忽略错误

gcc10编译安装glibc 2.27

CFLAGS="-g -fcommon -Og" CXXFLAGS="-g -fcommon -Og" ../glibc-2.27/configure --prefix=/glibc/2.27 --with-headers=/usr/include --with-binutils=/usr/bin --disable-werror 

我编译的时候报了重复定义的错误,加-fcommon 忽视

how2heap

first_fit

first-fit behavior.If a chunk is free and large enough, malloc will select this chunk.

Whenever any chunk (not a fast chunk) is freed, it ends up in the unsorted bin. Insertion happens at the HEAD of the list.

fast chunks end up in fastbins. As mentioned earlier, fastbins maintain a singly linked list and chunks are inserted and deleted from the HEAD end。头插法

calc::

索引计算公式::

The basic formula is as follows:

        IDX = (CHUNKSIZE - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT
        On a 64 bit system the current values are:
                MINSIZE: 0x20
                MALLOC_ALIGNMENT: 0x10
        So we get the following equation:
        IDX = (CHUNKSIZE - 0x11) / 0x10

chunksize计算公式::

It is calculated as follows:
        IF x + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE(0x20) CHUNKSIZE = MINSIZE (0x20)
        ELSE: CHUNKSIZE = (x + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) 
        => CHUNKSIZE = (x + 0x8 + 0xf) & ~0xf

64位下,将最低四位置0,因为size 总是16的倍数,要对齐

当 req=24 时,request2size(24)=32。0x18+0x8+0xf & ~0xf = 0x20而除去 chunk 头部的 16 个字节。实际上用户可用 chunk 的字节数为 16。而根据我们前面学到的知识可以知道 chunk 的 pre_size 仅当它的前一块处于释放状态时才起作用。所以用户这时候其实还可以使用下一个 chunk 的 prev_size 字段,正好 24 个字节

fastbin_dup.c

**double-free **
https://guyinatuxedo.github.io/27-edit_free_chunk/double_free_explanation/index.html
同一地址释放两次,再分配时,可以得到同一地址的两个指针,释放一个指针,但可通过另外一个指针操作释放的这个相同块,放入一些数据

https://blog.csdn.net/chennbnbnb/article/details/109284780
glibc2.31 版本下double free
tcache全称是Thead Cache

首先要填充tcache bin,再分配释放后到 fastbin

fastbin_dup_into_stack.c 2.23

在2.31下触发free(): double free detected in tcache 2

fastbin 是单链,只有fd指针,指向下一个块
通过修改fd指针,指向一个伪造的fake chunk

gef➤  x/15gx 0x602010-0x10
0x602000:   0x0000000000000000  0x0000000000000021 <-- chunk a [be freed twice]
0x602010:   0x0000000000602020  0x0000000000000000      <-- fd pointer
0x602020:   0x0000000000000000  0x0000000000000021 <-- chunk b [be freed]
0x602030:   0x0000000000602000  0x0000000000000000      <-- fd pointer
0x602040:   0x0000000000000000  0x0000000000000021 <-- chunk c
0x602050:   0x4343434343434343  0x0000000000000000
0x602060:   0x0000000000000000  0x0000000000020fa1
0x602070:   0x0000000000000000

unsigned long long *d = malloc(8);
stack_var = 0x20;
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

fastbin 的fd指针指向下一个空闲块的 metadata开始的地址处
malloc 返回的地址指向的是user data开始处,d是double free后的重复块地址,所以上面的赋值是 将fake_chunk的metadata起始地址 赋给 块中的fd指针,因为这是 d 是double free,虽然分配了,但也指向空闲块中,将chUnk fd 指针指向一个 fake chunk 的metadata起始地址处,即prev_size ; size=20; userdata。

Fastbins[idx=0, size=0x20]  ←  Chunk(addr=0x55555555a010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x7fffffffdd50, size=0x20, flags=)  ←  Chunk(addr=0x55555555a020, size=0x0, flags=) [incorrect fastbin_index] 

可以看到成功

typedef struct var newtype;
struct var 的别名为newtype、

heap_consolidation

Editing freed chunks will allow us to overwrite heap metadata, which is crucial to a lot of attacks.
edit a freed chunk using a heap overflow bug to cause consolidation

overwrite the size to be 0x510, essentially clearing the previous in use bit. This way when we free this chunk, it will think that the previous chunk has been freed

这样就再分配后,就有两个指针指向同一个块

改写当前size中 is_prev位,使得释放当前块后,认为前一个块是free的
heap_overflow

ptr2 = malloc(0x500);
ptr2[-1] 指示size
ptr[-2] 指示prev_size

fastbin_dup_consolidate.c 2.23
https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/3.1.6_heap_exploit_1.html#fastbin_dup_consolidate
利用在 large bin 的分配中 malloc_consolidate 机制绕过 fastbin 对 double free 的检查

malloc(0x400) small bins(回收最大值)后,fastbin 中的跑到 small bins中
[+] small_bins[4]: fw=0x55555555b000, bk=0x55555555b000
→ Chunk(addr=0x55555555b010, size=0x50, flags=PREV_INUSE)

2.23 里malloc_consolidate,先回收 fastbins 里的到unsorted bin中,因为这个bin满足不了分配要来,按照大小 又到了small bins中,也有可能到达large bins中。

此时可以再free() 一次,因为该块不在fastbin,释放后归到fastbins中。

这样我们就相当于double free

UAF

一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer

内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。

释放块后,指向其的指针不清除,所以可以再用这个指针写入数据

babyheap 2017

有点莽,说实话不是很懂
https://ctf-wiki.org/pwn/linux/glibc-heap/fastbin_attack/#2017-0ctf-babyheap
https://guyinatuxedo.github.io/28-fastbin_attack/0ctf_babyheap/index.html
https://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html

GNU/Linux 2.6.32 对应着glibc 2.23版本

┌─[zentreisender@parrotos]─[~/Documents/heap/题目]
└──╼ $file /glibc/2.23/lib/libc-2.23.so 
/glibc/2.23/lib/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /glibc/2.23/lib/ld-linux-x86-64.so.2, BuildID[sha1]=eb6c32093c3e8e3b1c03a382947ec4151d036d75, for GNU/Linux 2.6.32, with debug_info, not stripped

思路

The first will involve causing heap consolidation to get a libc infoleak. The second will involve using a Fastbin Attack to write a oneshot gadget to the hooc of malloc. The libc infoleak will allow us to break ASLR in libc and know the address of everything, and writing over the malloc hook with a ROP gadget (that will call system) will give us a shell when we call malloc (we need the infoleak to figure out where the malloc hook and rop gadget are)
也可以使用chunck overlap泄露unsorted bin的地址


题目不是原本的相同实现方法,但思想是不变的
开启了relrc,不能修改got;alloc pointers in a random mmap area. Therefore, we do not know the address to launch the “unsafe unlink” attack


知识

v6 = __readfsqword(0x28u);
Read memory from a location specified by an offset relative to the beginning of the GS segment.
https://docs.microsoft.com/en-us/cpp/intrinsics/readgsbyte-readgsdword-readgsqword-readgsword?view=msvc-160

void* calloc (size_t num, size_t size);
Allocate and zero-initialize array
Size of each element.
pData = (int*) calloc (i,sizeof(int));

fill 中size由自己定义,存在漏洞, the vulnerability is evident. We can fill the arbitrary length of input to the heap and overflow anything after that.

$readelf -s /glibc/2.23/lib/libc-2.23.so | grep __malloc_hook

我们想要做的是 泄露libc的基址,Heap overlap.
Overlapping two chunks to leak the address of the libc

将 chunk 4 释放掉,其 fd 指针会被设置为指向 unsorted bin 链表的头部,这个地址在 libc 中,且相对位置固定,利用它就可以算出 libc 被加载的地址
这时新的chunk2也指向fd , bk

调用 malloc() 时,首先判断 hook 函数指针是否为空,不为空则调用它。所以这里我们传入一个 one-gadget。
https://github.com/david942j/one_gadget

gef➤ x/10gx (long long)(&main_arena)-0x30