参考:https://www.anquanke.com/post/id/241598
次要参考:https://xz.aliyun.com/t/6342
malloc_chunk 的源码如下:
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /*前一个chunk的大小*/
INTERNAL_SIZE_T size; /*当前chunk的大小*/
struct malloc_chunk * fd; /*指向前一个释放的chunk*/
struct malloc_chunk * bk; /*指向后一个释放的chunk*/
}
释放的chunk 会以单向链表的形式回收到fastbin 里面。
fastbin 是 LIFO 的数据结构,使用单向链表实现。
示例代码:
// https://www.anquanke.com/post/id/241598
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
puts("The goal of this is to show how we can edit a freed chunk using a Double Free bug.");
puts("Editing freed chunks will allow us to overwrite heap metadata, which is crucial to a lot of heap attacks.");
puts("However a bug to edit the heap metadata is often just one piece of the exploitation process.\n");
printf("So we start off by allocating three chunks of memory. Let's also write some data to them.\n\n");
char *ptr0, *ptr1, *ptr2;
ptr0 = malloc(0x30);
ptr1 = malloc(0x30);
ptr2 = malloc(0x30);
char *data0 = "00000000";
char *data1 = "11111111";
char *data2 = "22222222";
memcpy(ptr0, data0, 0x8);
memcpy(ptr1, data1, 0x8);
memcpy(ptr2, data2, 0x8);
printf("Chunk0: @ %p\t contains: %s\n", ptr0, ptr0);
printf("Chunk1: @ %p\t contains: %s\n", ptr1, ptr1);
printf("Chunk2: @ %p\t contains: %s\n\n", ptr2, ptr2);
printf("Now is where the bug comes in. We will free the same pointer twice (the first chunk pointed to by ptr0).\n");
printf("In between the two frees, we will free a different pointer. This is because in several different versions of malloc, there is a double free check \n(however in libc-2.27 it will hit the tcache and this will be fine).\n");
printf("It will check if the pointer being free is the same as the last chunk freed, and if it is the program will cease execution.\n");
printf("To bypass this, we can just free something in between the two frees to the same pointer.\n\n");
free(ptr0); //-----------------------> b1
free(ptr1);
free(ptr0); //-----------------------> b2
printf("Next up we will allocate three new chunks of the same size that we freed, and write some data to them. This will give us the three chunks we freed.\n\n");
char *ptr3, *ptr4, *ptr5;
ptr3 = malloc(0x30); //--------------> b3
ptr4 = malloc(0x30);
ptr5 = malloc(0x30);
memcpy(ptr3, data0, 0x8);
memcpy(ptr4, data1, 0x8);
memcpy(ptr5, data2, 0x8);
printf("Chunk3: @ %p\t contains: %s\n", ptr3, ptr3); //-------------> b4
printf("Chunk4: @ %p\t contains: %s\n", ptr4, ptr4);
printf("Chunk5: @ %p\t contains: %s\n\n", ptr5, ptr5);
printf("So you can see that we allocated the same pointer twice, as a result of freeing the same pointer twice (since malloc will reuse freed chunks of similar sizes for performance boosts).\n");
printf("Now we can free one of the pointers to either Chunk 3 or 5 (ptr3 or ptr5), and clear out the pointer. We will still have a pointer remaining to the same memory chunk, which will now be freed.\n");
printf("As a result we can use the double free to edit a freed chunk. Let's see it in action by freeing Chunk3 and setting the pointer equal to 0x0 (which is what's supposed to happen to prevent UAFs).\n\n");
free(ptr3);
ptr3 = 0x0;
printf("Chunk3: @ %p\n", ptr3);
printf("Chunk5: @ %p\n\n", ptr5);
printf("So you can see that we have freed ptr3 (Chunk 3) and discarded it's pointer. However we still have a pointer to it. Using that we can edit the freed chunk.\n\n");
char *data3 = "15935728";
memcpy(ptr5, data3, 0x8);
printf("Chunk5: @ %p\t contains: %s\n\n", ptr5, ptr5);
printf("Just like that, we were able to use a double free to edit a free chunk!\n");
}
需要使用glibc 2.27编译。
Linux下更换glibc版本的方法:https://blog.csdn.net/weixin_44864859/article/details/107237134
使用glibc-all-in-one和patchelf对编译好的二进制文件直接替换其ld和libc的链接库地址,指向2.27版本的再次进行调试。
下面进行解释。
首先是申请三段内存,并填入数据:
ptr0 = malloc(0x30);
ptr1 = malloc(0x30);
ptr2 = malloc(0x30);
char *data0 = "00000000";
char *data1 = "11111111";
char *data2 = "22222222";
memcpy(ptr0, data0, 0x8);
memcpy(ptr1, data1, 0x8);
memcpy(ptr2, data2, 0x8);
然后依次释放ptr0、ptr1、ptr0
(这里之所以要把ptr1插入进来,是因为直接连续两次释放ptr0的话,会被检测出来异常)
free(ptr0); //-----------------------> b1
free(ptr1);
free(ptr0); //-----------------------> b2
释放ptr0和ptr1之后的Tcachebin:
再释放ptr0之后的Tcachebin:
可以看到addr=0x555555758670这个chunk被放到了tcache 0x40 大小的链表上两次
之后再申请ptr3、ptr4、ptr5(同样大小)
char *ptr3, *ptr4, *ptr5;
ptr3 = malloc(0x30); //--------------> b3
ptr4 = malloc(0x30);
ptr5 = malloc(0x30);
memcpy(ptr3, data0, 0x8);
memcpy(ptr4, data1, 0x8);
memcpy(ptr5, data2, 0x8);
printf("Chunk3: @ %p\t contains: %s\n", ptr3, ptr3); //-------------> b4
printf("Chunk4: @ %p\t contains: %s\n", ptr4, ptr4);
printf("Chunk5: @ %p\t contains: %s\n\n", ptr5, ptr5);
可以看出来,ptr3和ptr5实际上是返回的同一块地址。
因此,在之后,即便我们释放ptr3,并且把ptr3的值指向0x0,我们还是可以操作这个已经被释放的chunk。
free(ptr3);
ptr3 = 0x0;
printf("Chunk3: @ %p\n", ptr3);
printf("Chunk5: @ %p\n\n", ptr5);
char *data3 = "15935728";
memcpy(ptr5, data3, 0x8);
printf("Chunk5: @ %p\t contains: %s\n\n", ptr5, ptr5);
总结就是2次free,2次malloc,一次free,最终得到可用的空闲块指针。
我们先不用管 修改已被释放的空闲块中的内容到底有什么用,
我们现在只需要知道 Double free可以实现这个目标 就可以了
Java中是否存在Double Free
- C/C++ 提供了 malloc 和 free两个函数,允许程序员自主管理内存,因此可能出现Double Free漏洞;
- Java、Python等语言处于内存安全的考虑,不允许用户自己管理内存,因此不存在Double Free漏洞。
(Java有自己的垃圾回收机制)
参考:https://cloud.tencent.com/developer/article/2246094
学习计划安排
我一共划分了六个阶段,但并不是说你得学完全部才能上手工作,对于一些初级岗位,学到第三四个阶段就足矣~
这里我整合并且整理成了一份【282G】的网络安全从零基础入门到进阶资料包,需要的小伙伴可以扫描下方CSDN官方合作二维码免费领取哦,无偿分享!!!
如果你对网络安全入门感兴趣,那么你需要的话可以
点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
①网络安全学习路线
②上百份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥HW护网行动经验总结
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析