技术社区
安全培训
技术社群
积分商城
先知平台
漏洞库
历史记录
清空历史记录
相关的动态
相关的文章
相关的用户
相关的圈子
相关的话题
注册
登录
CVE-2025-21893分析与复现
Cheater
发表于 北京
漏洞分析
2354浏览 · 2025-06-04 10:20
返回文档
0x00.前言
新版linux内核keyring框架的uaf漏洞,这个漏洞本质上是很简单的,但是触发条件极其苛刻,需要条件竞争(时机卡的很死),然后最后还有个调度问题(可能可以用别的方法绕过)。因此为了方便分析漏洞点的可触发性,这里我对内核源码进行了3处的只影响运行速度和时机的修改。
0x01.前置知识
本文使用的源码版本为linux6.13.2.
keyring
这个模块想必搞内核的师傅都了解过,user_key_payload利用就是使用的keyring相关的系统调用。
Linux 内核的
keyring
机制提供了一种
安全地存储和管理加密密钥、身份认证令牌、证书等敏感数据
的方式。它通过一组
系统调用
允许用户空间程序管理密钥,并提供内核内部组件(如文件系统、加密 API)对密钥的访问能力。
用户可利用add_key、keyctl、request_key等系统调用对密钥进行一些管理。
模块目录:security/keys/
add_key
顾名思义,add_key实际就是向指定的 keyring 添加一个新的密钥。
Plain Text
复制代码
参数包括
:
●
type
:密钥的类型(如
"user"
、
"logon"
)。
●
description
:密钥的描述,用于标识密钥。
●
payload
:密钥的数据内容。
●
plen
:密钥数据的长度。
●
keyring
:目标 keyring 的句柄,密钥将被添加到此 keyring(KEY_SPEC_USER_KEYRING、KEY_SPEC_PROCESS_KEYRING等)。
keyctl
keyctl和ioctl一样,提供多种管理密钥的功能。
功能包括
:
●
KEYCTL_GET_KEYRING_ID
:获取 keyring ID。
●
KEYCTL_JOIN_SESSION_KEYRING
:创建或加入 session keyring。
●
KEYCTL_REVOKE
:撤销密钥,使其不可用。
●
KEYCTL_DESCRIBE
:获取密钥的信息(如类型、描述)。
●
KEYCTL_CLEAR
:清空 keyring。
●
KEYCTL_LINK
:将密钥链接到 keyring。
●
KEYCTL_UNLINK
:从 keyring 移除密钥。
●
KEYCTL_INVALIDATE
:将key标记为无效key。
●
KEYCTL_SEARCH
:在 keyring 中搜索密钥。
●
KEYCTL_READ
:读取密钥数据。
●
KEYCTL_INSTANTIATE
:实例化密钥(通常用于
request_key
的
upcall
机制)。
request_key
查找现有密钥,若找不到,则尝试调用
upcall
机制获取密钥。
参数包括
:
●
type
:密钥的类型。
●
description
:密钥的描述。
●
callout_info
:可选参数,用于
upcall
机制(通常用于自动生成密钥)。
●
keyring
:目标 keyring 的句柄
Keyring结构
Keyring 是一种特殊的密钥,可以包含其他密钥。每个进程默认有三个 keyring:
●
进程 Keyring
:与进程绑定,在进程退出时销毁。
●
线程 Keyring
:与线程绑定,线程结束时销毁。
●
用户 Session Keyring
:与用户会话绑定,在用户登出时销毁。
●
User Keyring
:特定用户拥有的密钥集合。
●
Group Keyring
:用户组共享的密钥集合。
这里需要注意,当使用多线程时,只有
User Keyring
或者
Group Keyring
能线程间共享,后面多线程条件竞争的时候就踩了这个坑。
一个keyring可以拥有多个key,也就意味着我们可以通过add_key往一个keyring中添加多个key。
Keyring-GC
在security/keys/gc.c文件中存放着关于keyring框架的gc垃圾回收代码。
当进行一些操作如keyctl_invalidate或者key_put操作时,会使用schedule_work调用gc回收函数key_garbage_collector来进行释放操作。
0x02.漏洞分析
根据
http://lore.kernel.org/linux-cve-announce
网站可以看的该CVE的相关信息。
根据以上描述可以知道,漏洞出现在key_put函数,在将key->usage减为0之后,还会对key进行一些操作,而此时异步的gc处理函数是通过key->usage是否为0判断是否进行释放操作的,这样就可能会导致gc处理函数释放key后,key_put继续对key进行操作导致uaf。
根据issue commit可以看到diff信息。
这里能看到将gc对于key->usage的判断改成key->flags的判断,并在key_put操作完key后才设置这个flags值。
我们查看未修复的相关源码:
key_put函数在对key->usage减一后,只要减为0就会判断key是否KEY_FLAG_IN_QUOTA的flag类型,如果符合就会继续对key进行操作。最后调用schedule_work(&key_gc_work)触发gc回收来释放key。
可以看到key_gc_work注册的函数是key_garbage_collector。
再看key_garbage_collector函数,会对遍历树中的所有key,并检查key->usage,为0就会执行释放的代码。
这样很明显是存在问题的,因为key_garbage_collector并不只是会被key_put调用,包括keyctl_invalidate等函数也会调用。
keyctl_invalidate调用链如下:
所以我们只要能在key_put减引用与操作key之间,通过key_garbage_collector释放key即可造成uaf漏洞。当然这样的利用条件十分苛刻,太卡时机了。
0x03.漏洞触发
经过分析,我们可以了解到keyctl_invalidate可以触发gc回收函数,而key_put的调用就需要用到keyctl_unlink操作了。
先前我们add_key就是创建一个key并放入keyring中,而keyctl_unlink则是将key从keyring中删除。
通过如下调用链可触发key的减引用。
其中key_unlink会调用__key_unlink_begin函数创建删除key的edit命令。
之后__key_unlink调用assoc_array_apply_edit执行edit操作,即删除操作。
最后调用keyring_assoc_array_ops结构体的keyring_free_object调用key_put对key进行减引用。
这里需要注意assoc_array_apply_edit是通过call_rcu触发的keyring_free_object函数。
由此我们可以写出如下半成poc:
因为这里条件竞争很麻烦,所以我们需要修改内核代码保证竞争成功。poc里面的多线程就设置只需要执行一次。
其中我们修改的两处分别为key_put函数:
以及key_garbage_collector函数
因为我们这里使用的是for循环阻塞,所以需要在函数声明处加个
attribute((optimize("O0")))
来防止这段for循环被优化掉。
给key_garbage_collector一个小循环保证另一个线程能执行到key_put减引用之后,同时给key_put加一个大循环,保证在key_put执行后续对key的操作时,gc能来得急进行key的校验以及释放工作。
但只做以上两处修改你会发现poc并不管用,跟踪之后发现是因为gc最后卡在了synchronize_rcu函数。
可以看到found_unreferenced_key会把key放入graveyard,之后通过
key_gc_unused_keys(&graveyard)
来free掉key。
但是在key_gc_unused_keys调用之前,会调用synchronize_rcu函数等待rcu任务完成。
而又刚好我们前文unlink是通过call_rcu来调用key_put函数的,所以这里会等待key_put操作之后才会继续释放key。这样看来,key又变得十分安全了。
但是我们要知道key_put不只会被unlink的call_rcu调用,unlink在对key进行删除操作时,会优先给key加引用,之后结束时再减引用。
这里的lookup_user_key会自动给key加引用,在执行完key_unlink之后,又会直接调用key_ref_put来减引用。
如果我们能在call_rcu执行完key_put后再去调用key_ref_put来减引用就可以绕过rcu保护机制了,当然这样会比较困难,因为call_rcu是延迟释放。不过兴许可以通过其他对key进行操作的函数来实现。
这里我们就不再去深入探索了,索性直接在key_ref_put前加个synchronize_rcu来等call_rcu执行完(当然这里使用循环来造成延迟应该也是可以的)。
经过这三处地方的修改,再结合我们的半成poc即可实现uaf。
0x04.总结
该漏洞产生的核心原因在于:当进行异步的gc操作时,错误的将引用计数为0作为释放的判断条件,而又在减引用之后对准备释放的指针进行操作。
个人认为这是一个比较新的攻击面,很有可能在其他异步操作时出现相同的问题,有待深挖。
0
人收藏
0
人喜欢
转载
分享
0
条评论
某人
表情
可输入
255
字
评论
发布投稿
热门文章
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
近期热点
一周
月份
季度
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
暂无相关信息
暂无相关信息
优秀作者
1
T0daySeeker
贡献值:38700
2
一天
贡献值:24800
3
Yale
贡献值:18000
4
1674701160110592
贡献值:18000
5
1174735059082055
贡献值:16000
6
Loora1N
贡献值:13000
7
bkbqwq
贡献值:12800
8
手术刀
贡献值:11000
9
lufei
贡献值:11000
10
xsran
贡献值:10600
目录
0x00.前言
0x01.前置知识
keyring
add_key
keyctl
request_key
Keyring结构
Keyring-GC
0x02.漏洞分析
0x03.漏洞触发
0x04.总结
转载
标题
作者:
你好
http://www.a.com/asdsabdas
文章
转载
自
复制到剪贴板