Gemini 2.5 Pro DeepResearch

权力的核心:深入解析 Linux cred 结构体及其在提权中的利用

一. 引言:Linux 内核中的权力中枢

在 Linux 的安全模型中,一个进程的身份即是其权力的体现。本报告的核心论点是,struct cred 是 Linux 内核中用于封装此身份的权威数据结构。它不仅仅是标识符的集合,更是进程权力的根本来源。因此,对于已经获得内核空间初步立足点的攻击者而言,破坏或替换其进程的 cred 结构体,是获取系统最终控制权(即 root 权限)最直接、最明确的路径。

我们可以将 cred 结构体视为一个进程的“皇冠上的宝石”。几乎所有内核内的提权攻击,其最终目的都是为了实现以下三者之一:夺取这些宝石(替换 cred 指针)、伪造它们(覆写 cred 内容),或者欺骗内核授予一套全新的、合法的皇家珠宝(例如,通过调用 commit_creds)。

这种设计将特权集中到一个定义明确的单一结构中,是一把双刃剑。一方面,它简化了内核的访问控制逻辑;另一方面,它也为攻击者创造了一个高价值的、可被利用的单点故障。内核需要一种一致的方式来检查进程的每一个动作(如打开文件、发送信号)的权限 1。与其向每个安全检查函数传递数十个参数(如 UID、GID、能力等),内核将它们整合到由

task_struct 指向的单一 struct cred 中,这既高效又使安全模型标准化。然而,这一设计决策意味着,如果攻击者能够控制这个指针或其指向的数据,他们就能同时控制进程身份的所有方面。因此,一次成功的、针对 cred 的内存破坏,可以瞬间将一个非特权进程转变为超级用户,一次性绕过所有细粒度的权限检查。这使得它成为比逐个击破单个安全机制远为诱人的目标,也解释了为何几乎所有现代内核提权漏洞利用的最终目的都是以某种方式修改 cred 结构体。

本报告将系统性地剖析这一主题,首先从 cred 的解剖学结构入手,进而探讨其合法的管理机制,然后深入研究用于颠覆它的对抗性技术,通过分析真实世界的 CVE 案例来揭示攻击手法,并讨论内核的防御策略,最后以战略性洞见收尾。

二. 解构 struct cred:Linux 进程身份的解剖学

A. cred 结构体:定义与核心字段

struct cred 定义于内核头文件 cred.h 中,是 Linux 内核安全体系的基石。系统中的每一个进程(由 task_struct 结构体表示)都包含一个指向其自身 cred 结构体的指针,该结构体囊括了做出所有安全决策所需的全部信息。通过使用 Bootlin Elixir 等代码交叉引用工具,研究人员可以方便地在内核源码树中定位这些定义,从而进行深入探索。

为了理解攻击者试图修改的内容,以下表格详细列出了 cred 结构体中最关键的字段及其在 Linux 安全模型中的作用和重要性。

表 1:struct cred 的关键字段

字段 类型 来源 描述
usage atomic_t S2 引用计数器。只有当此计数值降至零时,该结构体才会被释放。
uid, gid kuid_t, kgid_t S2 真实用户/组 ID: 标识拥有该进程的实际用户。主要用于进程所有权和信号发送权限的判断。
suid, sgid kuid_t, kgid_t S2 保存的用户/组 ID: 用于在进程的有效 ID 临时改变时存储其原始值,以便后续恢复。对 setuid 程序的正常运作至关重要。
euid, egid kuid_t, kgid_t S2 有效用户/组 ID: 内核在进行大多数权限检查时使用的主要 ID。对于普通进程,euid 与 uid 相同。对于 setuid-root 程序,euid 为 0。这是攻击者的首要目标。
fsuid, fsgid kuid_t, kgid_t S2 文件系统用户/组 ID: 专门用于文件系统访问检查。在现代内核中,此值主要与 euid 同步,但其存在代表了访问控制的一个历史层次。
group_info struct group_info* S2 指向一个结构体的指针,该结构体包含进程所属的附加组列表。

B. 超越 UID:Linux 能力(Capabilities)的力量

在 Linux 内核 2.2 版本之前,权限模型是二元的:进程要么是 root(UID 为 0),拥有所有权限;要么不是,受到严格限制。为了实现更精细的权限控制,Linux 引入了“能力(Capabilities)”系统,将传统上与 root 用户绑定的、无所不能的权力分解为近 40 个独立的、可管理的权限单元。通过这种方式,系统可以仅授予一个进程完成其任务所必需的特定权限(例如,赋予 CAP_NET_ADMIN 以配置网络),而无需给予其完整的 root 访问权限,这体现了最小权限原则。

这一概念与 cred 结构体紧密相连,其权限由以下几个关键字段定义 2:

系统管理员可以使用 getcap 和 getpcaps 等工具来分别检查文件和运行中进程的能力。一个经典的例子是 ping 命令,它需要 CAP_NET_RAW 能力来创建原始套接字,但不需要完整的 root 权限。

能力系统虽然为安全加固而设计,却也无意中创造了一个更复杂、更细微的攻击面。攻击者不再总是需要成为 UID 0;获得一个强大的单一能力可能就足以达到目的。这种转变源于最小权限原则的推行,它避免了向系统服务授予完整的 root 权限。然而,这也分散了“特权”的定义。攻击者现在有了多条通往目标的路径,除了传统的 UID 0,他们还可以瞄准 CAP_SYS_ADMIN、CAP_DAC_OVERRIDE 等高权限能力。这在容器化环境中尤为重要。一个容器可能以“root”(在其命名空间内的 UID 0)身份运行,但其能力集却受到严格限制。一个成功的容器逃逸漏洞可能不会直接赋予攻击者宿主机的 root 权限,但可能会授予其额外的能力,这些能力随后可被串联利用以实现完全的权限提升。因此,安全模型不再是简单的 root 与 non-root 的二元对立,而是一个复杂的能力图谱,攻击者可以在这个图谱中寻找路径。

表 2:提权中具有高影响力的 Linux 能力

能力 来源 描述与利用价值
CAP_SYS_ADMIN S4 “上帝”能力。授予大量系统管理权限,实际上等同于 root,常被称为“新 root”。
CAP_DAC_OVERRIDE S4 绕过所有自主访问控制(DAC)检查。允许进程读、写、执行系统上的任何文件,无视其权限设置。
CAP_CHOWN S3, S4 允许更改任何文件的用户和组所有权。可用于获取对 /etc/shadow 等关键系统文件的控制权。
CAP_SETUID / CAP_SETGID S4 允许进程操纵其 UID/GID,从而可以有效地成为任何用户,包括 root。
CAP_SYS_MODULE (由 S3 推断) 允许加载和卸载内核模块。攻击者可利用此能力加载恶意的内核模块。
CAP_SYS_PTRACE (由 S11 推断) 允许使用 ptrace 跟踪任意进程。可用于劫持其他进程,包括特权进程。

三. 不可变性原则:合法的凭证管理

A. “复制并替换”的信条

内核安全的一个核心设计原则是:cred 结构体一旦被提交(即公开),就被认为是不可变的。这意味着不能简单地获取一个指向当前进程 cred 的指针然后直接修改其字段。为了改变凭证,内核必须遵循“复制并替换”(copy-and-replace)的方法论:首先分配一个新的 cred 结构体,复制旧结构体的内容,修改这个新的副本,然后原子性地将当前进程的 current->cred 指针交换为指向新结构体 1。这样做是为了防止竞态条件,并确保对安全敏感的数据结构的操作是原子性的,不会使其处于短暂的、不一致的状态。

B. 神圣三位一体:prepare_creds()、commit_creds() 和 abort_creds()

内核内部提供了一套 API 来安全地管理凭证变更,这套 API 由三个核心函数组成:

C. 攻击者的黄金入场券:commit_creds(prepare_kernel_cred(0))

本节的高潮在于揭示一个关键函数序列。prepare_kernel_cred(0) 是一个特殊的内核函数,当以 NULL 或 0 作为参数调用时,它会准备一个全新的凭证结构体,该结构体属于 init 任务——即系统中的第一个进程,拥有完整的 root 权限(UID 0、GID 0、所有能力)。

因此,commit_creds(prepare_kernel_cred(0)) 这个序列,正是内核自身用于让一个内核空间实体赋予自己完整 root 权限的、规范的、合法的方式。对于任何已经获得内核执行流控制权的攻击者来说,这个序列就成了他们的终极目标。他们的任务就是想方设法劫持 CPU,去执行这段现成的代码。

内核为安全管理凭证而设计的机制,矛盾地为攻击者创造了完美的、预先打包好的“漏洞利用载荷”。commit_creds(prepare_kernel_cred(0)) 的存在意味着攻击者无需费力地编写复杂的 Shellcode 来手动修改 cred 结构体;他们只需要找到一种方法来调用这两个现有的函数。内核需要一种合法的方式来创建新的、拥有完全特权的进程(例如在系统启动期间),或者让内核线程执行特权操作。prepare_kernel_cred(0) 和 commit_creds() 函数正是为此提供了必要的内部 API。这个 API 健壮、经过良好测试,并且保证在不同内核版本间都能正常工作。从攻击者的角度看,一旦他们通过漏洞获得了内核代码执行能力(例如,控制了指令指针),为何还要采用一种脆弱的、手动的、可能因内核微小更新而失效的方法去内存中寻找 cred 结构并覆写其字段呢?相比之下,找到 prepare_kernel_cred 和 commit_creds 函数的地址(可通过 KASLR 泄露实现),然后构建一个 ROP 链来依次调用它们,是一种远为可靠和优雅的方案。因此,这个专为安全的内部权限管理而设计的机制,本身却成为了漏洞利用中最受觊觎的目标,这种现象可称为内核空间的“就地取材”(Living off the Land)。

四. 攻击者的博弈:颠覆 cred 实现提权

A. 入口点:内核漏洞的前提条件

必须强调的是,cred 操纵是提权的载荷,而非漏洞本身。攻击者必须首先发现并利用一个独立的漏洞,才能在内核的地址空间内获得立足点。

释放后使用(Use-After-Free, UAF)作为典型漏洞:UAF 是一种常见的内存损坏漏洞。其过程包括:程序分配一块内存,随后释放它,但仍然保留着一个指向该已释放内存的“悬垂指针”。当程序后续通过这个悬垂指针访问内存时,它实际上访问的是一块可能已被重新分配用于其他目的的内存,这会导致数据损坏或控制流劫持。UAF 是一个强大的原语,因为它通常可以被转化为任意地址写或任意地址释放的能力,为后续的漏洞利用铺平道路。

B. 技术一:直接操纵与基于 ROP 的提权

C. 技术二(案例研究)- DirtyCred (CVE-2021-4154):在堆上交换身份

DirtyCred 是一种颠覆性的、纯数据的利用技术,它利用了内核堆分配器的弱点,而非特定的代码逻辑错误。

D. 技术三(案例研究)- Dirty Pipe (CVE-2022-0847):间接提权

虽然 Dirty Pipe 不直接操纵 cred,但它对于本讨论至关重要,因为其最终目标是创建一个拥有 root 凭证的新进程,展示了通往同一结果的另一条路径。

E. 综合攻击链(案例研究)- CVE-2023-3390

本节将通过一个完整的、现代的内核漏洞利用过程,来综合展示上述所有概念。

  1. 触发漏洞:分析 Netfilter (nftables) 子系统中的 UAF 漏洞,该漏洞可用于创建一个悬垂指针。
  2. 堆整理与对象操纵:攻击者精心分配和释放对象(通常使用 msg_msg 或其他“弹性对象”)来控制堆布局,确保一个受控对象能够占据被释放的易受攻击对象的槽位。这一阶段对漏洞利用的稳定性至关重要。
  3. KASLR 泄露:利用 UAF 造成信息泄露。通过将一个被回收对象中的指针覆写为一个已知值,攻击者可以读取相邻内存,其中可能包含一个内核指针。通过减去一个已知的偏移量,他们可以计算出被随机化的内核基地址,从而击败 KASLR。
  4. PC 控制与 ROP 链:在 KASLR 被绕过后,攻击者便知晓了 ROP 小工具和关键函数的地址。他们再次利用 UAF,这次是为了覆写一个被回收对象中的函数指针。当内核调用这个函数指针时,执行流就被劫持到了攻击者的 ROP 链。
  5. 终局之战 - commit_creds:ROP 链的唯一目的就是执行 commit_creds(prepare_kernel_cred(&init_task)),从而赋予当前进程 root 权限。
  6. 后利用与清理:漏洞利用成功后,通常会脱离任何容器命名空间(使用 setns),并在宿主机上生成一个 /bin/bash shell。

表 3:针对 cred 的漏洞利用技术对比

技术 前提漏洞 核心机制 可绕过的缓解措施
直接 cred 覆写 任意内核写 找到 current->cred 并将其 UID/GID 字段覆写为 0。 本身无法绕过。需要信息泄露来绕过 KASLR。
ROP 调用 commit_creds 栈溢出 / UAF / 类型混淆,导致 PC 控制 链接内核代码片段(小工具)来调用 commit_creds(prepare_kernel_cred(0))。 绕过 NX/DEP。需要信息泄露来绕过 KASLR。可被强 CFI 阻止。
DirtyCred UAF / 双重释放 通过堆操纵,将一个低权限的 cred 或 file 结构体与一个高权限的结构体交换。 绕过 KASLR、SMEP/SMAP 和 CFI,因为它是一种纯数据攻击。
Dirty Pipe (间接) 逻辑缺陷 (CVE-2022-0847) 覆写一个只读文件(如 SUID 程序、/etc/passwd)来创建一个新的 root 权限进程。 不是内存损坏攻击,因此绕过内存安全缓解措施。

五. 内核的防御:保护 cred 的军备竞赛

A. 混淆目标:内核地址空间布局随机化 (KASLR)

B. 强制边界:SMEP 与 SMAP

C. 内核加固的演进格局

攻防双方之间存在一场清晰的、不断升级的军备竞赛。内核开发者引入的每一层新防御,都直接导致了攻击者方法的转变,催生出更复杂、更隐蔽的利用技术。这个演进过程清晰地展示了一条因果链:

  1. 问题:攻击者将 Shellcode 放入栈中并跳转执行。
  2. 防御:NX/DEP 将栈标记为不可执行。
  3. 攻击者创新:攻击者转而将执行流重定向到用户空间内存。
  4. 防御:SMEP 阻止内核执行用户空间内存。
  5. 攻击者创新:ROP 技术被开发出来,仅使用现有内核代码,绕过 SMEP。
  6. 防御:KASLR 随机化 ROP 小工具和内核函数的地址。
  7. 攻击者创新:漏洞利用变为两阶段:首先找到信息泄露点绕过 KASLR,然后使用 ROP。
  8. 防御:KPTI 和其他加固措施使信息泄露更加困难。
  9. 攻击者创新:纯数据攻击如 DirtyCred 和 Dirty Pipe 出现,它们完全绕过了控制流劫持的范式。它们不关心 KASLR 或 SMEP,因为它们从不劫持执行流。
  10. 下一代防御:现在的焦点必须转向数据完整性和对象隔离,保护 cred 和其他结构体免遭损坏或交换,而不仅仅是保护指令指针。

六. 结论与战略建议

本报告详尽地阐述了 struct cred 作为 Linux 内核中进程权限的最终体现,其中心化的设计使其成为高效的访问控制工具,同时也沦为攻击者的首要目标。我们追溯了漏洞利用技术从直接内存覆写到复杂的 ROP 链,再到颠覆性的纯数据攻击(如 DirtyCred 和 Dirty Pipe)的演变。这种演变是与内核防御措施(如 KASLR 和 SMEP/SMAP)的平行发展直接对应的。

根本的冲突在于,像操作系统内核这样的安全关键组件,却使用了内存不安全的语言(C)来编写。像 UAF 这样的漏洞是所有后续 cred 操纵得以实现的根源。

对系统管理员的建议

对开发者与安全研究人员的建议

引用的著作

  1. Credentials in Linux — The Linux Kernel documentation, 访问时间为 六月 23, 2025, https://www.kernel.org/doc/html/v5.9/security/credentials.html
  2. Linux capabilities 101 - Linux Audit, 访问时间为 六月 23, 2025, https://linux-audit.com/kernel/capabilities/linux-capabilities-101/
  3. What is DirtyCred and how can it be mitigated? | CrowdStrike, 访问时间为 六月 23, 2025, https://www.crowdstrike.com/en-us/blog/what-is-the-dirtycred-exploit-technique/
  4. Kernel Self-Protection — The Linux Kernel documentation, 访问时间为 六月 23, 2025, https://www.kernel.org/doc/html/v6.5/security/self-protection.html?highlight=kaslr
  5. Kernel Level Protections: Supervisor Mode Execution Protection ..., 访问时间为 六月 23, 2025, https://www.seandeaton.com/smep/
  6. Supervisor Mode Access Prevention - Wikipedia, 访问时间为 六月 23, 2025, https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention