Linux内核动态内存隔离机制
2020年12月29日 | Alexander Popov
Linux内核中的释放后使用漏洞
Linux内核中的UAF(use-after-free)漏洞是攻击者最常利用的漏洞类型之一。存在大量公开的UAF内核漏洞利用原型:
- CVE-2016-8655
- CVE-2017-6074
- CVE-2017-2636
- CVE-2017-15649
- CVE-2019-18683
利用UAF通常采用堆喷技术(heap spraying),目的是将攻击者控制的数据放置到特定的动态内存区域(称为"堆")。Linux内核中利用UAF的堆喷技术基于kmalloc()调用时,slab分配器返回最近释放的内存地址:
![堆喷技术示意图]
通过创建相同大小的可控内核对象,可以覆盖已释放的易受攻击对象:
![对象覆盖示意图]
技术方案
2020年7月,我产生了对抗Linux内核UAF堆喷技术的想法。8月份开始实验,从KASAN功能中分离出slab分配器的隔离机制,命名为SLAB_QUARANTINE。
启用该机制后,释放的内存分配会被放入隔离队列等待实际释放,因此无法被UAF漏洞利用立即重新分配和覆盖:
![隔离机制示意图]
SLAB_QUARANTINE安全特性
为了研究内核动态内存隔离的安全特性,我开发了两个lkdtm测试:
1. lkdtm_HEAP_SPRAY测试
|
|
禁用CONFIG_SLAB_QUARANTINE时,释放的对象立即被重新分配:
|
|
启用隔离后,40万次分配不会覆盖释放的对象:
|
|
2. lkdtm_PUSH_THROUGH_QUARANTINE测试
该测试通过40万次分配释放操作推动对象通过隔离队列,结果显示对象在特定尝试次数后被重新分配:
|
|
随机化改进
为了解决重分配尝试次数相对稳定的问题,我开发了隔离随机化机制。对象在隔离区中以"批次"存储,释放时随机选择批次和对象:
|
|
性能测试
在不同配置下进行性能测试:
-
iperf网络吞吐量测试:
- init_on_free=on 比 off 低28%
- SLAB_QUARANTINE 比 init_on_free=on 低2%
-
hackbench调度器负载测试:
- init_on_free=on 慢5.3%
- SLAB_QUARANTINE 慢91.7%(物理机)/ 44%(虚拟机)
-
内核编译测试:
- init_on_free=on 慢1.7%
- SLAB_QUARANTINE 慢1.1%
绕过攻击
Jann Horn提出了有效的绕过方法:攻击者可以使用其他slab缓存进行大量分配释放操作,冲刷隔离队列使目标对象返回分配器的空闲列表,然后使用标准堆喷技术利用UAF。
结论
虽然未能创建出进入mainline的可靠防护机制,但这项研究提供了有价值的结果和思路,将为后续Linux内核防护工作提供参考。
Quarantine patch version three
Won’t appear. No need.
Let’s exploit use-after-free
Like we always did ;)
— a13xp0p0v