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).
复制时 没有大小检查,且是分配在堆上
Unlink() explannation
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
. 要是全局变量
- 要有溢出漏洞
- 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的值指针化后赋给 tempprintf("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=ptr0fake->fd = target-0x18 =FD
减0x18是因为下面是取 bk
因为 FD-> bk = fake FD的其他结构值都不重要,只要bk值,这里就是&target,这个地址保存着fake chunk metadata的开始地址,即ptr0target-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 写操作
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
.
unlink
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表中内容,即实际地址值