一、说明UAF
-
二、UAF漏洞利用
#include <stdio.h>
#include <stdlib.h>
typedef void (*func_ptr)(char *);
void evil_fuc(char command[])
{
system(command);
}
void echo(char content[])
{
printf("%s",content);
}
int main()
{
func_ptr *p1=(func_ptr*)malloc(4*sizeof(int));
printf("malloc addr: %p\n",p1);
p1[3]=echo;
p1[3]("hello world\n");
free(p1); //在这里free了p1,但并未将p1置空,导致后续可以再使用p1指针
p1[3]("hello again\n"); //p1指针未被置空,虽然free了,但仍可使用.
func_ptr *p2=(func_ptr*)malloc(4*sizeof(int));//malloc在free一块内存后,再次申请同样大小的指针会把刚刚释放的内存分配出来.
printf("malloc addr: %p\n",p2);
printf("malloc addr: %p\n",p1);//p2与p1指针指向的内存为同一地址
p2[3]=evil_fuc; //在这里将p1指针里面保存的echo函数指针覆盖成为了evil_func指针.
p1[3]("/bin/sh");
return 0;
}
结果:
-
三、修复方法
预防UAF漏洞的方法包括使用适当的内存管理实践,例如在释放内存后将指针设置为NULL
,以及使用AddressSanitizer等工具来检测和修复此类问题
置空指针,避免悬挂指针
P1=null
-
四、根本原因
第一次申请的内存空间在释放过后没有进行内存回收,导致下次申请内存的时候再次使用该内存块,使得以前的内存指针可以访问修改过的内存
应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放会内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(这是主要原因)。当dlmalloc中空闲内存量达到一定值时dlmalloc才将空闲内存释放会内核。如果应用程序申请的内存大于256kb,dlmalloc调用mmap()向内核申请一块内存,返回返还给应用程序使用。如果应用程序释放的内存大于256kb,dlmalloc马上调用munmap()释放内存。dlmalloc不会缓存大于256kb的内存块,因为这样的内存块太大了,最好不要长期占用这么大的内存资源。
延迟释放小内存块:对于小于256KB的内存块,dlmalloc通常会将其标记为空闲状态而不是立即返回给操作系统。这是由于性能的,因为间隙的内存释放和申请可能会导致内存碎片。通过在设备中保留一些小内存块分配,可以更有效地处理小规模的内存分配请求。
不立即释放到内核:即使free被调用,dlmalloc也不一定会立即将内存块返回给操作系统。这是因为释放的内存块可能不在堆的顶部,释放内存到操作系统可能会涉及到移动其他内存块,从而导致性能开销。
mmap用于大内存块:如果应用程序请求的内存块大于256KB,dlmalloc通常会使用mmap等系统调用向操作系统请求一个新的内存区域。这样的内存分配通常更适合大型对象,因为它们可以被有效地分配和释放,而不会导致堆内存的碎片化。
munmap用于释放大内存块:对于释放大内存块,dlmalloc通常会使用munmap或类似的系统调用将内存返还给操作系统。这有助于释放对应用程序地址空间的大内存块的占用。
总体来说,dlmalloc设计的目标是在性能和内存利用效率之间找到一个平衡点,以满足不同类型应用程序的需求。理解内存管理器的工作原理对于开发者来说是很重要的,尤其是在需要处理方面大规模内存操作或需要考虑应用程序的性能
原文链接:https://blog.csdn.net/qq_31481187/article/details/73612451