2024 L3HCTF Kpid
翻出一道历史悠久的题
漏洞分析
ioctl功能
三个功能:
0x47001:调用kernel_clone,并记录pid结构体指针pid和pid号nr
实际上和用户态调用fork效果一样(驱动代码如下)
1
2
3
4args.exit_signal = 17LL;
memset(&args.stack, 0, 88);
memset(&args, 0, 32);
nr = kernel_clone(&args);fork系统调用定义,SIGCHLD就是17
1
2
3
4
5
6
7
8SYSCALL_DEFINE0(fork)
{
struct kernel_clone_args args = {
.exit_signal = SIGCHLD, // 17
};
return kernel_clone(&args);
}
0x58002:获取nr,也就是子进程号,没什么好说的
0x69003:调用了put_pid,会将pid->count也就是引用计数减一
1
2
3
4
5
6
7
8
9
10
11
12
13
14void put_pid(struct pid *pid)
{
struct pid_namespace *ns;
if (!pid)
return;
ns = pid->numbers[pid->level].ns;
if (refcount_dec_and_test(&pid->count)) {
kmem_cache_free(ns->pid_cachep, pid);
put_pid_ns(ns);
}
}
EXPORT_SYMBOL_GPL(put_pid);减到0就会将这个pid从对应ns里删除并释放
UAF
可以通过0x69003功能释放pid结构体
利用方法1
Cross Cache Attack
pid结构体的分配释放使用专有cache,所以需要将攻击目标结构体转换到其他cache
slab内存管理数据结构图:

分配逻辑:
- 走kmem_cache_cpu
- 走freelist
- 当kmem_cache_cpu->page为空时,遍历kmem_cache_cpu->partial,摘下一个slab放入kmem_cache_cpu->page
- 走kmem_cache_node
- 遍历kmem_cache_node->partial,摘下最多kmem_cache->cpu_partial / 2个slab放入kmem_cache_cpu->partial
- 申请新的slab,放入kmem_cache_cpu->page

释放逻辑:
- 对象属于kmem_cache_cpu->page,直接放回
- 对象属于kmem_cache_cpu->partial,直接放回
- 对象不属于kmem_cache_cpu->partial,且所属slab从full变为partial
- kmem_cache_cpu->partial容量不超过kmem_cache->cpu_partial,将slab放入kmem_cache_cpu->partial
- kmem_cache_cpu->partial容量超过kmem_cache->cpu_partial,将kmem_cache_cpu->partial中slab放入kmem_cache_node->partial,将slab放入kmem_cache_cpu->partial
- 对象不属于kmem_cache_cpu->partial,且所属slab从partial变为empty
- kmem_cache_node->partial容量不超过kmem_cache->min_partial,将slab放入kmem_cache_node->partial
- kmem_cache_node->partial容量超过kmem_cache->min_partial,将slab放回buddy system
- 对象属于kmem_cache_node->partial,直接放回

Cross Cache Attack需要将攻击目标结构体所属slab放回buddy system,等待分配给另一个cache,相当于一个漏洞的转移
Dirty Pagetable
可以通过增加或减少进程的refcount来控制pid->count,选择PTE作为攻击目标
1 |
|
增加PTE 0x1000可以让两个PTE指向同一个page,造成UAF。可以通过判断页内容来找dirty page

利用流程
喷PTE消耗一波page,然后申请DMA-BUF,然后再喷一波PTE,这样能使DMA-BUF page之后相邻PTE page

增加PTE 0x1000,造UAF page

munmap UAF page,再mmap到DMA-BUF

再增加PTE 0x1000,使dirty PTE指向另一个PTE页

然后就可以通过UAF page改PTE表了,0x9c000泄露内核加载物理基址,然后改__sys_setresuid函数内容
Exp
1 |
|
利用方法2
比较简单的一种,通过add timer增加pid->count,使PTE从只读变为可写,改busybox
Exp
1 |
|
2024 L3HCTF Kpid
http://akaieurus.github.io/2025/08/14/kpid/