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

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29
    53                 return 0;
    54         }
    55
    56         ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
    57         mmput(mm);
    58
    59         return ret;
    60 }
    kernel/capability.c
    499 bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
    500 {
    501         int ret = 0;  /* An absent tracer adds no restrictions */
    502         const struct cred *cred;
    503         rcu_read_lock();
    504         cred = rcu_dereference(tsk->ptracer_cred);
    505         if (cred)
    506                 ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
    507         rcu_read_unlock();
    508         return (ret == 0);
    509 }
如上, ptrace_access_vm 函数会调用我们之前分析到的 ‘ptracer_capable’ 来决定这个 request 是否可以进行, 这是 ‘ptracer_capable’ 函数的第二种使用场景
根据之前我们分析的结果, task C 此时保存的 ptracer_cred 是特权 cred, 所以这时候 ptracer_capable 会通过, 也就是说我们回答了刚刚的问题, 这种情况下,普通权限的 task B 是可以发送 ptrace request 去读写 root 权限的 task C 的内存区和代码区的
至此,task C 记录的这个特权 ptracer_cred 实际上发挥了 2 种作用
1,可以让 task C 执行 execve(suid binary) 给自己提权
2,可以让普通权限的 task B 执行 ptrace 读写 task C 的代码区和内存区,从而控制 task C 执行任意操作
上面 2 点合起来,不就是完整的提权操作吗?
 
小结
我们仔细回顾上述代码分析过程, 才终于明白补丁描述写的这段话
PTRACE_TRACEME records the parent's credentials as if the parent was
acting as the subject, but that's not the case.  If a malicious
unprivileged child uses PTRACE_TRACEME and the parent is privileged, and
at a later point, the parent process becomes attacker-controlled
(because it drops privileges and calls execve()), the attacker ends up
with control over two processes with a privileged ptrace relationship,
which can be abused to ptrace a suid binary and obtain root privileges.
本质上这个漏洞有点像 TOCTOU 类漏洞, ptracer_cred 的获取是在 traceme 阶段, 而 ptracer_cred 的应用是在随后的各种 request 阶段, 而在随后的 ptrace request 的时候, tracer 的 cred 可能已经不是一开始建立 trace link 时的那个 cred 了
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 8456b6e..705887f 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -79,9 +79,7 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
  */
 static void ptrace_link(struct task_struct *child, struct task_struct *new_parent)
 {
-    rcu_read_lock();
-    __ptrace_link(child, new_parent, __task_cred(new_parent));
-    rcu_read_unlock();
+    __ptrace_link(child, new_parent, current_cred());
 }
我们再次看看 jann horn 的补丁: ‘__task_cred(new_parent)’ -> ‘current_cred()’
补丁的意思是说在 PTRACE_TRACEME 这种场景下, ptracer_cred 记录的不应该是父进程的 cred, 而应该是自己的 cred
所以我觉得从这个变量的用途来说,它其实记录的不是 tracer 的 cred, 而是 ‘trace link creater’ 的 cred
我建议 jann horn 将这个变量名改成 ptracelinkcreater_cred, 当 trace link 由 PTRACE_ATTACH 建立时, 它等于 tracer 的 cred, 当 trace link 由 PTRACE_TRACEME 建立时, 它等于 tracee 的 cred, 它实际上记录的是 trace 关系建立者的权限 !
 
exploit
本漏洞利用的关键是找到合适的可执行程序启动 task B, 这个可执行程序要满足如下条件:
1, 必须是能被普通权限用户调用
2, 执行时必须有提权到root的阶段
3, 执行提权后必须执行降权
(短暂提权到 root 的目的是让 task C 可以获取 root 的 ptracer_cred, 再降权的目的是让 B 能被普通权限的进程 ptrace attach)
这里我列出 3 份 exploit 代码:
1 jann horn 的 exploit
2 bcoles 的 exploit
3 jiayy 的 exploit
jann horn 的 exploit 里使用桌面发行版自带的 pkexec 程序用于启动 task B
pkexec 允许特权用户以其他用户权限执行另外一个可执行程序, 用于 polkit 认证框架, 当使用 —user 参数时, 刚好可以让进程先提权到 root 然后再降权到指定用户,因此可以用于构建进程 B, 此外需要找到通过 polkit 框架执行的可执行程序(jann horn 把他们成为 helper), 这些 helper 程序需要满足普通用户用 pkexec 执行它们时不需要认证(很多通过 polkit 执行的程序都需要弹窗认证), 执行的模式如下:

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