欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

CVE-2019-13272 'PTRACE_TRACEME' 本地提权漏洞分析(二)

来源:本站整理 作者:佚名 时间:2019-08-08 TAG: 我要投稿

前面一篇文章介绍了CVE-2019-13272引发的第一个问题,通过race 可以导致系统 panic,接着上面一篇文章,这篇文章会介绍cve-2019-13272提到的第二个问题,它可以通过 suid 程序达到本地提权的目的
由于自身水平有限,有些地方可能理解不当,望指正。
 
漏洞原理
jannh 给出的利用逻辑如下对于一个普通用户,考虑下面场景
task A fork 出task B
task B fork 出task C
B execve 一个 suid 程序(假设root,这时 B 的 cred是 root 权限)
C 调用 PTRACE_TRACEME 让 B trace 自己 ( C 记录 B root 权限的 cred)
C execve 同样执行一个 suid 程序(/usr/bin/passwd)
B 减低自己权限 (关键 这个时候 A 可以通过 ptrace 修改 B 的内存)
C 保存的 ptracer 的 cred 是 root权限的,也就是说它会认为父进程是一个root权限的进程,而本身C已经是 root权限了,这个时候 B就可以修改 C的内存达到用以root权限任意执行代码了。
这个过程有两个问题
ptrace的时候是怎么样做的权限检查?
怎么样找出 一个 task B 这样的 suid 程序?
pkexec 程序
首先回答第二个问题polkit 是 linux桌面下的一个授权模块,大多数的linux发行版本都有这个模块。因为自己也不是十分熟悉,就不误人子弟了,可以参考这个漏洞
pkexec 可以通过 --user 参数授权特定的用户执行命令,没有指定的话默认为root权限
参考 exp 中 exec 的命令,我们用strace 看看
% strace pkexec --user rtfingc /usr/lib/gnome-settings-daemon/gsd-backlight-helper  --help 2>&1 |grep setre
setreuid(0, 0)                          = 0
setregid(1001, 1001)                    = 0
setreuid(1001, 1001)                    = 0
/usr/lib/gnome-settings-daemon/gsd-backlight-helper 是一个 elf 程序,上面这条命令相当于用 rtfingc 这个用户执行/usr/lib/gnome-settings-daemon/gsd-backlight-helper --help 这条命令转换到 rtfingc 用户的过程中会调用setreuid setregid 降低权限,这也就符合了前面 task B 的降权需求
ptrace 权限检查
首先从 ptrace 的使用上看
普通用户只能trace自己的进程
root 用户可以trace 其他用户的进程
同一个时间只能由一个ptracerptrace 有两种方式
trace其他进程(PTRACE_ATTACH/PTRACE_SEIZE)(也就是 attach)
让父进程trace自己(PTRACE_TRACEME)(只有子进程能用)
源码地址
我们假设 ptracer 是 B, ptracee 是 Cptrace traceme
ptrace_traceme 在上一篇文章我们已经有做了简单的分析ptrace_traceme() -> if (!current->ptrace) 是否已经被trace -> ptrace_link -> child->ptracer_cred = get_cred(ptracer_cred);
C 调用 PTRACE_TRACEME, 完成之后 C 的 ptracer_cred == B 的 cred
因为这里是进程自己授权其他进程来trace自己,所以并没有很多的检查大部分的检查都会在做内存操作的时候体现
做内存操作时
对 ptracee 做内存读写的时候, 会通过ptrace_access_vm 函数做检查
// tsk 是ptracee 的task_struck (C)
int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
             void *buf, int len, unsigned int gup_flags)
{
    struct mm_struct *mm;
    int ret;
    mm = get_task_mm(tsk);
    if (!mm)
        return 0;
// 是否已经被trace
    if (!tsk->ptrace ||
    // ptracer 是 current
        (current != tsk->parent) ||
        ((get_dumpable(mm) != SUID_DUMP_USER) &&
         !ptracer_capable(tsk, mm->user_ns))) {
        mmput(mm);
        return 0;
    }
    ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);
    mmput(mm);
    return ret;
}
ptracer_capable 函数
// task 为 ptracee 的 task_struct
bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
{
    int ret = 0;  /* An absent tracer adds no restrictions */
    const struct cred *cred;
    rcu_read_lock();
    // 获取 ptracer_cred
    cred = rcu_dereference(tsk->ptracer_cred);
    if (cred)
    // 检查时否符合
        ret = security_capable(cred, ns, CAP_SYS_PTRACE,
                       CAP_OPT_NOAUDIT);
    rcu_read_unlock();
    return (ret == 0);
}
这里获取了ptracee 的·ptracer_cred ,调用 PTRACE_TRACEME 的时候保存的可以是 suid 程序的 cred, 也就是 root 权限,后续会使用这个cred检查是否有trace的权限。
 

[1] [2] [3] [4] [5] [6] [7]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载