0x00 基本信息
- 信息总览:NVD 链接
- 漏洞披露:bugs.chromium.org
- 漏洞评估:CVSS 7.8
- 上游补丁:链接
0x01 详细描述
在 selinux_ptrace_traceme
函数(即 SELinux 对 PTRACE_TRACEME
的 LSM 接口的实现)中发现一个 Use-After-Free 问题,该问题是由于错误地尝试获取其他内核任务的*主体凭证(Subjective Credential)*而导致的。具体情况如下:
原先在 SELinux 中,有个函数 task_sid()
可以获取一个内核任务的*客体凭证(Objective Credential)*所对应的 SID,但却被用在了一些应该使用主体凭证的地方。为此,eb1231f73c4d (“selinux: clarify task subjective and objective credentials”)
引入了两个新函数 task_sid_subj()
和 task_sid_obj()
来替代 task_sid()
,并在上述场景中改为使用 task_sid_subj()
以获取主体凭证。
但这一改动是有问题的:当入参 struct task_struct *task
指向的是其他内核任务,该函数就是去获取其他任务的主体凭证。然而,一个任务的主体凭证默认是只应由该任务自身获取的1,因此在如 access_override_creds()
等一些代码中对主体凭证的修改和释放没有施加 RCU 保护(即便 struct task_struct
中代表主体凭证的成员 cred
标明了 __rcu
),从而使得在多进程场景中可能触发 Use-After-Free 问题。
相关函数调用栈
-
内存分配:
1# fs/open.c 2do_faccessat 3 access_override_creds # set subj_cred with non_rcu=1 4# kernel/cred.c 5 prepare_creds 6# security/security.c 7 security_prepare_creds 8 lsm_cred_alloc # cred->security = kzalloc(...)
-
内存释放:
1# fs/open.c 2do_faccessat 3# kernel/cred.c 4 revert_creds 5# include/linux/cred.h 6 put_cred 7# kernel/cred.c 8 __put_cred 9 put_cred_rcu 10# security/security.c 11 security_cred_free # kfree(cred->security)
-
问题触发:
1# kernel/ptrace.c 2ptrace_traceme 3# security/security.c 4 security_ptrace_traceme 5# security/selinux/hooks.c 6 selinux_ptrace_traceme 7 task_sid_subj 8 cred_sid # UAF: de-ref. tsec with tsec->sid
0x02 复现方法
- 准备一套仅合入了问题补丁且未合入修补补丁的环境,开启了 SELinux
- 创建一对父子进程
- 父进程循环调用
faccessat
系统调用 - 子进程调用首入参为 PTRACE_TRACEME 的
ptrace
系统调用 - 有一定几率会触发 UAF 问题
POC 代码及其他 KASAN 复现详情见披露信息 。
0x03 修复方式
将 SELinux(及 Smack)中涉及访问其他任务主体凭证的 task_sid_subj()
改为 task_sid_obj()
。详情见社区讨论和修补补丁。
-
因为一个内核任务的主体凭证本身代表的就是该任务作为一个动作的主体所拥有的权限。若是被其他任务获取,那说明当前场景下其他任务是主体,该任务肯定不是主体,逻辑上就矛盾了。因此“去获取其他内核任务的主体凭证”这一行为在语义上就是毫无意义且自相矛盾的。 ↩︎