作者:wzt
原文链接:https://mp.weixin.qq.com/s/20ACZFyQiUWZf5cIm_ZW-w
1.1 简介
Freebsd的内核内存分配器叫做UMA(Universal Memory Allocator),这篇文章只关心它的安全特性,对于常规功能实现请读者朋友参考网络上的其他文章。它的安全功能特性相比XNU、NT、LINUX都少了很多,并且还存在一些不安全的构架设计,下面将会详细分析。
1.2 架构设计缺点
UMA的总体架构也是基于solaris slab, 我们直接看最底层的slab结构,一个slab大小为PAGE_SIZE,slab的管理体结构依据slab里的每个item大小而决定,对于小块item,slab管理结构体放在slab里,并且是放到PAGE_SIZE的最后。对于大块item,管理结构体则单独分配一个内存,不包含在slab里。
对于小块item, slab这种设计属于严重的安全错误设计,slab header放在所有item的最后,如果最后一个item发生溢出,就可以直接覆盖slab header里的数据结构。
struct uma_slab { uma_keg_t us_keg; /* Keg we live in */ ... }
Slab header结构为struct Uma_slab,它的第一个成员是us_keg。
struct uma_keg { LIST_HEAD(,uma_zone) uk_zones; /* Keg's zones */ ... }
Uk_zones结构为:
struct uma_zone { uma_ctor uz_ctor; /* Constructor for each allocation */ uma_dtor uz_dtor; }
结构体成员uz_ctor和uz_dtor为每个zone在创建和销毁时调用的析构函数指针,exploit程序一般都会替换这两个函数指针,使其指向shellcode地址。Slab header放在最后,使堆溢出攻击相对linux变得更加简单, 因为linux的slab header就是放在最前面的。我们在设计内存分配器时就要避免这个糟糕的设计,同时管理结构体中函数指针的定义一定要做到最少,防止被exploit程序滥用。
1.3 安全特性缺失
1.3.1 溢出检测
能检测到溢出情况的发生是每个内存分配器的基础安全能力,业界的通用算法是在内存区块的前后加入redzone,在初始化时填充一个固定值,在内存释放时检测redzone里的固定值是否有改变来判断是否有溢出行为的发生。
UMA的redzone结构为:
在data的前面分别为struct stack 保存的是当前栈信息,size为data的大小,0x42则为redzone的固定值,一共16字节。在data的最后同样为16字节的固定值。
由于设置redzone会占用更多的内存,同时会使初始化和释放逻辑变得复杂,从而影响效率,所以检测溢出的发生都是作为debug选项来开启的。几乎所有主流的os内核内存分配器在设置redzone时都使用了固定值,笔者认为这也是一个不安全的设计,exploit编写者可以精心构造内存结构,使其shellcode地址指向0x42424242,就可以绕过检测。
1.3.2 UAF检测
对于UAF(Use After Free)的检测,freebsd没有实现这个功能,一般算法是在slab item释放时对data区域填充固定的值,在分配时先检测固定值有没有被污染,以此来判断有无UAF的发生。
1.3.3 双向安全链表
双向链表的删除操作时,要先检测前后节点是否为合法地址, freebsd同样没有设计这个功能。
1.3.4 item地址随机化
Slab里保存的item为了初始化简单, 每个item都是顺序链接的, 这给exploit程序的利用提供了极大的方便。Linux内核使用了洗牌算法将item的链接顺序打乱来规避这种攻击。Freebsd没有提供这个功能。
1.3.5 管理结构体cookie
同样为了检测是否有内存破坏的现象, 通常会在一些管理结构体中加入一个随机化的cookie值,内存释放时判断cookie是否有被污染。freebsd没有提供这个功能。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1451/