PTRACE_TRACEME CVE-2019-13272 本地提权漏洞解析

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29
}
void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
                   const struct cred *ptracer_cred)
{
        BUG_ON(!list_empty(&child->ptrace_entry));
        list_add(&child->ptrace_entry, &new_parent->ptraced); // 1. 将自己加入父进程的 ptraced 队列
        child->parent = new_parent; // 2. 将父进程地址保存在 parent 指针
        child->ptracer_cred = get_cred(ptracer_cred); // 3. 保存 ptracer_cred, 我们只关注这个变量
}
建立 trace 关系的关键是由 tracee 记录 tracer 的 cred, 保存在 tracee 的 ‘ptracer_cred’ 变量,这个变量名很顾名思义
ptracer_cred 这个概念是由 2016 年的一个补丁 ptrace: Capture the ptracer’s creds not PT_PTRACE_CAP 引入的, 引入 ptracer_cred 的目的是用于当 tracee 执行 exec 去加载 setuid executable 时做安全检测
为什么需要这个安全检测呢?
exec 函数族可以更新进程的镜像, 如果被执行文件的 setuid 位 置位,则运行这个可执行文件时,进程的 euid 会被修改成该可执行文件的所有者的 uid, 如果可执行文件的所有者权限比调用 exec 的进程高, 运行这类 setuid executable 会有提权的效果
假如执行 exec 的进程本身是一个 tracee, 当它执行了 setuid executable 提权之后,由于 tracer 可以随时修改 tracee 的寄存器和内存,这时候低权限的 tracer 就可以控制 tracee 去执行越权操作
作为内核,显然是不允许这样的越权行为存在的,所以当 trace 关系建立时, tracee 需要保存 tracer 的 cred (即 ptracer_cred), 然后在执行 exec 过程中, 如果发现执行的可执行程序是 setuid 位 置位的, 则会判断 ‘ptracer_cred’ 的权限, 如果权限不满足,将不会执行 setuid 位 的提权, 而是以原有的进程权限执行这个 setuid executable
这个过程的代码分析如下(本文的代码分析基于 v4.19-rc8)
do_execve
  -> __do_execve_file
  -> prepare_binprm
      -> bprm_fill_uid
      -> security_bprm_set_creds
          ->cap_bprm_set_creds
          -> ptracer_capable
          ->selinux_bprm_set_creds
          ->(apparmor_bprm_set_creds)
          ->(smack_bprm_set_creds)
          ->(tomoyo_bprm_set_creds)
如上,execve 权限相关的操作主要在函数 ‘prepare_binprm’ 里
    1567 int prepare_binprm(struct linux_binprm *bprm)
    1568 {
    1569         int retval;
    1570         loff_t pos = 0;
    1571
    1572         bprm_fill_uid(bprm); //
    1573
    1574         /* fill in binprm security blob */
    1575         retval = security_bprm_set_creds(bprm); //
                             // 可能会修改新进程的 cred
    1576         if (retval)
    1577                 return retval;
    1578         bprm->called_set_creds = 1;
    1579
    1580         memset(bprm->buf, 0, BINPRM_BUF_SIZE);
    1581         return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
    1582 }
如上,先调用 ‘bprm_fill_uid’ 初步填充新进程的 cred, 再调用 ‘security_bprm_set_creds’ 做安全检测并修改新的 cred
    1509 static void bprm_fill_uid(struct linux_binprm *bprm)
    1510 {
    1511         struct inode *inode;
    1512         unsigned int mode;
    1513         kuid_t uid;

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]  下一页