CVE-2021-43057 分析

0x00 基本信息

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 复现方法

  1. 准备一套仅合入了问题补丁且未合入修补补丁的环境,开启了 SELinux
  2. 创建一对父子进程
  3. 父进程循环调用 faccessat 系统调用
  4. 子进程调用首入参为 PTRACE_TRACEME 的 ptrace 系统调用
  5. 有一定几率会触发 UAF 问题

POC 代码及其他 KASAN 复现详情见披露信息

0x03 修复方式

将 SELinux(及 Smack)中涉及访问其他任务主体凭证的 task_sid_subj() 改为 task_sid_obj()。详情见社区讨论和修补补丁。


  1. 因为一个内核任务的主体凭证本身代表的就是该任务作为一个动作的主体所拥有的权限。若是被其他任务获取,那说明当前场景下其他任务是主体,该任务肯定不是主体,逻辑上就矛盾了。因此“去获取其他内核任务的主体凭证”这一行为在语义上就是毫无意义且自相矛盾的。 ↩︎


关于 SELinux 的宽泛介绍
LSM 的 Security Blob 机制