Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2019-13272 — CVE-2019-13272 的深入分析与利用,这是一个 Linux 内核 ptrace 权限提升漏洞。包含代码讲解与利用场景。 | Kitploit
工具/GitHubGitHub/datntsec/cve-2019-13272
权限提升漏洞分析漏洞利用学习与教育二进制利用
GitHubdatntsec/cve-2019-13272

CVE-2019-13272

CVE-2019-13272 的深入分析与利用,这是一个 Linux 内核 ptrace 权限提升漏洞。包含代码讲解与利用场景。

查看仓库
75年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2019-13272

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

PTRACE_TRACEME 是一个 Linux 内核中的权限提升漏洞,由 Jann Horn 于 2019 年 7 月发现。

漏洞分析:

Ptrace 是一个系统调用(system call),它提供了一种方法,允许一个进程(tracer)观察和控制另一个进程(tracee)的执行过程,检查并修改其核心映像(core image)和寄存器,主要用于在调试中设置断点(break point)以及跟踪系统调用的调用过程。``` c 1 396 kernel/ptrace.c <<ptrace_attach>> ptrace_link(task, current); 2 469 kernel/ptrace.c <<ptrace_traceme>> ptrace_link(current, current->real_parent);

有两种方法可以建立跟踪关系(trace relationship):
  - 进程调用 fork 函数,其子进程会调用 `PTRACE_TRACEME`(对应内核中的 `ptrace_traceme` 函数)来初始化 tracee。
  - 进程调用 `PTRACE_ATTACH` 或 `PTRACE_SEIZE`(对应内核中的 `ptrace_attach` 函数)来初始化一个 tracer,用于跟踪其他进程。

无论使用哪种方式,最终都会调用 `ptrace_link` 函数来在 tracer 和 tracee 之间建立跟踪关系。
- 对于 `ptrace_attach`,传入 `ptrace_link` 的两个参数是 'task'(tracee)和 'current'(tracer)。
- 对于 `ptrace_traceme`,传入 `ptrace_link` 的两个参数是 'current'(tracee)和 'current->real_parent'(tracer)。

在这里,我们需要留意上面两种方式中调用 `ptrace_link` 时传入的 tracer 和 tracee 这两个参数分别是什么,因为漏洞将位于 `ptrace_link` 函数中。``` c
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();
}

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. thêm chính nó vào hàng đợi 
                                                                 // ptraced của process cha
        child->parent = new_parent; // 2. Lưu địa chỉ của process cha trong con trỏ parent
        child->ptracer_cred = get_cred(ptracer_cred); // 3. Lưu ptracer_cred lại, ta cần tập trung 
                                                          // vào biến này vì lỗi nằm ở đây
}

建立 trace relationship 的关键在于,tracee 会记录 tracer 的 cred,并将其保存在 tracee 的 'ptracer_cred' 变量中。

'ptracer_cred' 的概念由 2016 年引入的一个补丁提出,ptrace: Capture the ptracer's creds not PT_PTRACE_CAP。引入 'ptracer_cred' 的目的是为了在 tracee 执行 exec 加载 setuid 可执行文件 时进行安全检查。

为什么我们需要检查这种安全性?

exec 系列函数可以更新进程映像。如果可执行文件的 setuid 位 被设置,那么当该可执行文件运行时,进程的 euid 将被修改为可执行文件所有者的 uid。进程的权限高于调用 exec 的用户的权限,运行这类 setuid 可执行文件 会产生权限提升(escalation)的效果。

想象一下,如果执行 exec 的进程本身是一个 tracee,那么当它执行 setuid 可执行文件 来提升特权后,它的 tracer 可以随时修改它(tracee)的寄存器和内存。如果低特权的 tracer 能够控制高特权的 tracee,那么 tracer 就可以通过 tracee 执行未授权操作。

然而,在内核中,似乎不允许存在这种越权行为。因此,在建立 trace relationship 时,tracee 需要保存 tracer 的 cred(即 ptracer_cred)。如果 tracee 执行一个 exec 进程,它会检查所运行的可执行文件的 setuid 位是否被设置;如果已设置,就会检查 'ptracer_cred' 的权限。如果权限不满足,则不会使用 setuid 位的执行权限(文件所有者的特权)来执行 exec,而是以原始用户的权限来执行。

该过程的代码分析如下(本文的代码分析基于 v4.19-rc8)。``` python 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)

与执行权限相关的操作主要位于 `prepare_binprm` 函数中``` c
1567 int prepare_binprm(struct linux_binprm *bprm)
1568 {
1569         int retval;
1570         loff_t pos = 0;
1571 
1572         bprm_fill_uid(bprm); // <-- fill cred của new process (xem hàm bprm_fill_uid bên dưới sẽ rõ hơn)
1573 
1574         /* fill in binprm security blob */
1575         retval = security_bprm_set_creds(bprm); // <-- kiểm tra bảo mật, để xem xét sửa đổi cred của new process     
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。``` c 1509 static void bprm_fill_uid(struct linux_binprm *bprm) 1510 { 1511 struct inode inode; 1512 unsigned int mode; 1513 kuid_t uid; 1514 kgid_t gid; 1515 1516 / 1517 * Since this can be called multiple times (via prepare_binprm), 1518 * we must clear any previous work done when setting set[ug]id 1519 * bits from any earlier bprm->file uses (for example when run 1520 * first for a setuid script then again for its interpreter). 1521 / 1522 bprm->cred->euid = current_euid(); // <--- trước tiên sẽ sử dụng euid của process hiện tại 1523 bprm->cred->egid = current_egid(); 1524 1525 if (!mnt_may_suid(bprm->file->f_path.mnt)) 1526 return; 1527 1528 if (task_no_new_privs(current)) 1529 return; 1530 1531 inode = bprm->file->f_path.dentry->d_inode; 1532 mode = READ_ONCE(inode->i_mode); 1533 if (!(mode & (S_ISUID|S_ISGID))) // <---------- nếu bit setuid/setgid của file thực thi không được set 1534 return; // , hàm sẽ return tại đây. 1535 1536 / Be careful if suid/sgid is set / 1537 inode_lock(inode); 1538 1539 / reload atomically mode/uid/gid now that lock held / 1540 mode = inode->i_mode; 1541 uid = inode->i_uid; // <---- nếu S_ISUID được set,sử dụng i_uid của file 1542 gid = inode->i_gid; 1543 inode_unlock(inode); 1544 1545 / We ignore suid/sgid if there are no mappings for them in the ns */ 1546 if (!kuid_has_mapping(bprm->cred->user_ns, uid) || 1547 !kgid_has_mapping(bprm->cred->user_ns, gid)) 1548 return; 1549 1550 if (mode & S_ISUID) { 1551 bprm->per_clear |= PER_CLEAR_ON_SETID; 1552 bprm->cred->euid = uid; // <------ sử dụng uid của file như là euid của new process 1553 } 1554 1555 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { 1556 bprm->per_clear |= PER_CLEAR_ON_SETID; 1557 bprm->cred->egid = gid; 1558 } 1559 }

看看上面代码的以下两行代码:
- 第 1522 行,将进程当前的 euid 赋给新的 euid,因此大多数执行进程都以原始权限执行。
- 第 1552 行,如果设置了 suid 位,则将可执行文件所有者的 uid 赋给新的 uid。可以将其理解为类似 setuid 的操作。新的 euid 变成可执行文件所有者的 uid,如果所有者是特权用户,则权限提升就在这里发生。

然而,这里的 euid 仍然不是最终结果,我们需要查看 `security_bprm_set_creds` 函数以了解更多关于安全检查的信息。

`security_bprm_set_creds` 函数调用 [LSM](https://en.wikipedia.org/wiki/Linux_Security_Modules) 框架

在我分析的 kernel 版本中,有多达 5 个 LSM 框架的 hook 点会对 `bprm_set_creds` 执行安全检查。检查函数如下:``` python
cap_bprm_set_creds
selinux_bprm_set_creds
apparmor_bprm_set_creds
smack_bprm_set_creds
tomoyo_bprm_set_creds

哪些 hook 函数会在此处执行将取决于每个特定内核的配置。理论上,如果所有 LSM 框架都被启用,上述所有 hook 函数都将被实现以检查 'bprm_set_creds'。

在我的分析环境中,只有 cap_bprm_set_creds 和 selinux_bprm_set_creds 两个 hook 函数运行。

下载工具