heap入门2

命令

objdump -M intel -D ./heap0 | grep winner
gef➤ help search-pattern

nightmare

protostar:heap0

objdump -D heap0 | grep winner

┌─[zentreisender@parrotos]─[~/Documents/nightmare/modules/24-heap_overflow/protostar_heap0]
└──╼ $objdump -M intel -D ./heap0 | grep winner
080484b6 <winner>:
080484e1 <nowinner>:

简单的堆溢出,复制时没有检查大小

protostar:heap1

strcpy 没有检查大小
第一次strcpy进行溢出,覆盖地址为got表中put地址,第二次向puts地址写入winner()函数地址,接下来调用puts()的时候,实际上调用到winner()

gef➤  help search-pattern 
SearchPatternCommand: search a pattern in memory. If given an hex value (starting with 0x)

可以用来搜索内存中值,打印位置

protostar:heap2

%p 打印地址

    00100ab3 48 8d 85        LEA        RAX=>local_98,[RBP + -0x90]
             70 ff ff ff
    00100aba 48 83 c0 07     ADD        RAX,0x7

The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory for the new string is obtained with malloc(3), and can be freed with free(3).

复制时 没有大小检查,且是分配在堆上

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

什么是unlink攻击

The most common scenario is a vulnerable buffer that can be overflow and has a global pointer. 要是全局变量

  1. 要有溢出漏洞
  2. chunk地址存在一指针数组中,且是全局变量

unlink() 是将双向链表的 块拿出来, 就需要重新调整 fd 和 bk 指针
例如 free 时和目前物理相邻的 free chunk 进行合并
It handles the process of overwriting pointers from the next and previous chunks to the other, to fill in the gap from taking out the chunk in the middle.

第一个检查::

The first check we need to worry about, is it checks if the Fd and Bk pointers of our fake heap chunk (they point to the next and previous chunks) point to chunks that have pointers back to our fake chunk.


构建一个fake chunk,这个chunk 可以在已申请的chunk中构建,因为我们可以写入值。
temp = (uint64_t *)ptr0[2]; 将fd的值指针化后赋给 temp
printf("Fd->bk: \t\t%p\n", (void *)temp[3]);
%p 按十六进制输出数据

void * 为”无类型指针”

内存分配函数 malloc 函数返回的指针就是 void * 型,用户在使用这个指针的时候,要进行强制类型转换,也就是显式说明该指针指向的内存中是存放的什么类型的数据 (int )malloc(1024) 表示强制规定 malloc 返回的 void 指针指向的内存中存放的是一个个的 int 型数据。


fake_chunk fd bk

fd 在0x10 偏移处,bk 在0x18偏移处
fake= ptr0
target=ptr0
fake->fd = target-0x18 =FD 减0x18是因为下面是取 bk
因为 FD-> bk = fake FD的其他结构值都不重要,只要bk值,这里就是&target,这个地址保存着fake chunk metadata的开始地址,即ptr0
target-0x18+0x18 = fake chunk = ptr0

同理

fake->bk=target-0x10 =BK
BK->fd = fake = ptr0
target-0x10 + 0x10 =ptr0

we will edit the heap metadata of the second chunk, so that it will say that the previous chunk has been freed and it points to our fake chunk.
只要设置prev_size,到metadata开始处,就认为是其前一个块

Then when we free the second chunk, it will cause our fake chunk to be unlinked and execute the pointer write.
will check that the size of our chunk is equal to the previous size of the chunk being freed
利用堆溢出 来修改


The final check we have to worry about is for fd_nextsize. Essentially it just checks to see if it is equal to 0x0, and if it is it skips a bunch of checks.
现在不为0才跳过一些检查

chunk结构

struct malloc_chunk {
  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

双向链表结构的bin中,fd_nextsize 指向下一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
bk_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
就是 bk 接下来的8字节,

unlink操作如下::

FD = fake->fd;
BK = fake->bk;
FD->bk = BK
BK->fd = FD

那么最后,BK->fd = FD
Fd pointer: 0x55d9452cf038
Bk pointer: 0x55d9452cf040
Bk->Fd: 0x55d946d5d420 即target地址存储的值
*target=fd=0x55d9452cf038

https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/3.1.6_heap_exploit_1.html#unsafe_unlink
这时 chunk0_ptr 和 chunk0_ptr[3] 实际上就是同一东西。这里可能会有疑惑为什么这两个东西是一样的,因为 chunk0_ptr 指针在是放在数据段上的,地址在 0x601070指向 0x601058,而 chunk0_ptr[3] 的意思是从 chunk0_ptr 指向的地方开始数 3 个单位,所以 *0x601058+0x083=0x601070**

所以给chunk0[3] 赋值 就相当于给chunk赋值,最后chunk[0] 为改写地址处的内容
chunk0是指针(重要)
&chunk0[3]==&chunk0

nightmare HITCON’14: stkof

00602100
Our exploitation process will contain two parts. The first will be doing an Unlink Attack, and the second will be a GOT overwrite / infoleak.

Unlinking for the heap is the process of removing a chunk from a bin list (in this case for heap consolidation for performance improvement reasons).

What this attack will do is give us a write.
However there are a lot of restrictions on what we can write and where we can write.
Essentially when an unlink happens, it will write pointers to a chunk to fill in the gap of the chunk that was taken out.
这就是写的来源
因为最后BK->fd=FD

本题中指针ptr,所在是一个指针数组,数组中的内容是指针,指向其他地方
首先我们要有一个指向 一个chunk块的指针ptr,来使得bk->fd 和 fd->bk指向fake chunk
题目中malloc后地址

                /* 将块地址保存在这里 */
*(void **)(&DAT_00602140 + (long)(int)DAT_00602100 * 8) = pvVar1;


    004009a8 48 98           CDQE
    004009aa 48 8b 55 88     MOV        RDX,qword ptr [RBP + local_80]
                         将块地址保存在这里
    004009ae 48 89 14        MOV        qword ptr [DAT_00602140 + RAX*0x8],RDX
             c5 40 21 
             60 00

动态运行下就知道地址

 →   0x4009ae                  mov    QWORD PTR [rax*8+0x602140], rdx
此时$rax   : 0x1               
所以保存在地址0x602148

接着来设置 fake chunk 的fd , bk指针

所以先分配两个块,这两个块不一定要相邻在一起,相邻可以简单地使用堆溢出,也好融合,然后在第一个块中创建fake chunk, 在free第二个块后,触发Unlink

那么fake chunk fd=ptr-0x18   , fd->bk=ptr
fake chunk bk=ptr-0x10 , bk ->fd=ptr

然后是设置size, pre_size和size要相符,然后是fd_nextsize == null (在glibc2.23下)

unlink 后
*ptr=ptr-0x8*3
可以用来got覆写

GOT Overwrite / Infoleak

现在有了一个指向指针数组的指针
就可以 向数组中写入指针


https://github.com/Finsenty54/attack-code/blob/master/heap/Hitcon_2014_stkof/myexploit.py
代码以及注解

zctf 2016 note2

把名字放在这里006020e0
最多四个块


DAT_00602120分配的块的地址放在这里,8字节,顺序排列
相应块的输入值大小放在DAT_00602140

这些位置在bss中,全局未初始化变量

Chunk(addr=0x108c2a0, size=0x90, flags=PREV_INUSE)
    [0x000000000108c2a0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x108c330, size=0x90, flags=PREV_INUSE)
    [0x000000000108c330     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x108c3c0, size=0x90, flags=PREV_INUSE)
    [0x000000000108c3c0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x108c450, size=0x90, flags=PREV_INUSE)
    [0x000000000108c450     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x108c4e0, size=0x20b30, flags=PREV_INUSE)  ←  top chunk
gef➤  x/32gx 0x00602120
0x602120:    0x000000000108c2a0    0x000000000108c330
0x602130:    0x000000000108c3c0    0x000000000108c450

自带的libc-2.19.so 太老了,使用2.23

overwrite_append() 函数里有数组长度检查,没有堆溢出
教程这里,将输入的长度设为0x00,造成整数溢出??因为确实0x00-1=0xffffffff,同时设置成0x00,还是会分配0x20的空间

The first will hold our fake chunk for the unlink. The second chunk we will use to overflow the metadata of the third chunk. The third chunk will be the one which we overwrite the heap metadata to point to the fake chunk, and we free it.
只用修改prev_size


0x23ce320:    0x0000000000000000    0x0000000000000021
0x23ce330:    0x3535353535353535    0x3535353535353535
0x23ce340:    0x00000000000000a0    0x0000000000000090
0x23ce350:    0x3131313131313100    0x3131313131313131

溢出

https://github.com/Finsenty54/attack-code/blob/master/heap/zctf_2016_note2/exploit.py
攻击代码
泄露got表中内容,即实际地址值