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

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

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

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2022-1015-1016 — 由 David 发现并记录的 CVE-2022-1015 和 1016 的西班牙语翻译。 | Kitploit
工具/GitHubGitHub/zanezhub/cve-2022-1015-1016
权限提升漏洞分析漏洞利用学习与教育二进制利用
GitHubzanezhub/cve-2022-1015-1016

CVE-2022-1015-1016

由 David 发现并记录的 CVE-2022-1015 和 1016 的西班牙语翻译。

查看仓库
164年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
网站

CVE-2022-1015 & CVE-2022-1026

Este README.md es una traducción del blog de David. David encontró los CVE's 1015 y 1016 en el kernel de Linux. Puedes visitar su página web para leer el documento original.

Aquí te dejo sus redes sociales:

  • Twitter
  • Github

对 nf_tables 中两个新的 Linux 漏洞的分析

发布于 2022 年 4 月 2 日。

  • CVE-2022-1015 允许执行由输入参数验证不充分导致的越界(out-of-bounds)访问,可能造成远程代码执行和本地权限提升。
  • CVE-2022-1016 与栈上变量初始化不充分有关,可被用来将大量内核数据泄露到用户空间(userspace)。

在最新版 Ubuntu 和 RHEL 的默认配置中,这些问题应该都是可利用的。我针对 Arch Linux 的 5.16-rc3 内核版本编写了 CVE-2022-1015 的概念验证(PoC)。

本文面向对 Linux 内核的功能和安全性有基本了解的人。我试图让本文对缺乏网络栈知识的人友好,以便向所有读者开放。

以下是一份阅读指南:

  • 如果你只是想了解漏洞,请从第 4 节开始
  • 如果你还想了解一些内核子系统的背景,请从第 2 节开始
  • 如果你对更多背景感兴趣,请阅读整篇文档

1. 背景

2 月中旬,Google 安全计划宣布将继续其 kCTF 漏洞奖励计划,为能够在 nsjail 沙箱中从无特权进程将权限提升至 root 用户的 Linux 内核漏洞提供从 31,337 美元到 91,337 美元不等的奖励。

作为一个贫穷的学生,这显然引起了我的注意。这是我第一次尝试寻找“现实世界”的漏洞,但在和我的团队一起玩 CTF 的冒险中,我已经从安全角度熟悉了 Linux 内核。在数小时几乎毫无进展(但对 Linux 有了更多了解)之后,我设法在 nf_tables 模块中发现了一些漏洞。

遗憾的是,最终我意识到这个模块并不在 Google kCTF 的规则范围内(因此这两个漏洞没有为我赢得任何奖励)。但显然,我仍然报告了它们,并为 CVE-2022-1015 编写了一个 LPE(本地权限提升)漏洞利用。

1.1 确定目标与审计策略

好了,你已经决定要在 Linux 中找一些漏洞。现在呢?Linux 是一个巨大的项目,很容易只见树木不见森林(过于关注细节而失去对真正重要事物的整体视野)。更糟糕的是,许多部分没有文档,你需要阅读大量代码才能理解正在发生的事情。

我首先尝试详细了解 Linux 的安全模型。找到一个 bug 是一回事;找到一个好 bug 则是另一回事。毕竟,并非所有 bug 都是同等的:

  • 如果某个 bug 需要 root 权限,那么就不存在有意义的安全边界(除非启用了 内核模块签名)
    • 我想到的是一些(虚拟)文件系统模块。只有初始 root 用户才能挂载这些文件系统。例外是 vfe,它指定了 FS_USERNS_MOUNT,在这种情况下你可以将它们挂载到 用户命名空间 中。
  • 如果无法通过系统调用访问某个 bug,那么它很可能不可利用。
    • 这适用于许多硬件驱动程序,因为你无法物理访问机器。底层网络驱动程序仍然可能是好目标,如果你可以 例如 通过蓝牙或 802.11.ac 发送数据。
    • 显然这取决于你所在的场景。
  • 许多 bug 需要 CAP_SYS_ADMIN 或 CAP_NET_ADMIN。
    • 用户命名空间 默认启用,所以这不是问题。
    • 否则,你首先必须在容器内将权限提升到 命名空间 的 root 用户。
  • 并非所有模块都会出现在你的目标中。
    • Linux 是一个高度可配置的出色软件,因此所有配置都可能以多种方式变化。
    • 内核配置通常可以从 /proc/config.gz 访问。模块可以编译为内置(=y),也可以(=m)编译并在运行时加载。
    • 你可以使用 /proc/modules 和 /proc/kallsyms,但并不总是可靠,因为模块可以在内核中动态加载(例如 request_module)。
    • 如果不确定,写一个小程序尝试与模块交互。

这些限制帮助我们了解可以在哪些文件系统边界中寻找漏洞。我认为花时间规划对目标目标的攻击是个好主意。

我已经从上面这一点吸取了教训。如前所述,nf_tables 模块并未加载到 kCTF 给我们的实例中。我本可以一开始就注意到这一点,从而避免失望 :p。另一方面,如果早点注意到,你现在可能就不会读这篇博客了,我想事情最终发展得还不错。

关于为何 COS(Google 的容器优化 Linux 分支)没有 nf_tables 的解释可以在这里和这里找到。

1.2 nf_tables:为什么?

在评估了上述各点之后,我决定开始的最佳途径可能是查看网络源代码。那里许多有趣的功能需要 CAP_NET_ADMIN,但如前所述,这实际上不是问题。相反,我怀疑需要特殊权限的组件通常安全性较低,因为内核开发者可能有一种虚假的安全感。

我还努力选择了一个我想要更多了解的文件系统;这样,即使你找不到任何 bug,你仍然可以学到很多有趣的东西。

我研究了许多网络文件系统,但没有发现任何重要的东西。在浏览 net/ 子目录后,我遇到了 nf_tables 模块。它看起来有点复杂,所以我决定花点时间去了解它。

2. Netfilter 简介

Netfilter(net/netfilter)是内核中一个相当大的网络文件系统子系统。简而言之,netfilter 在整个网络模块中放置钩子(hook),其他模块可以注册处理程序。当到达某个钩子时,控制权被交给这些处理程序,它们可以操作各自的网络数据包结构。处理程序可以接受、丢弃和修改数据包。


4. CVE-2022-1015

在花了几个小时浏览 nf_tables API(net/netfilter/nf_tables_api.c)以准确了解其工作原理之后,我决定检查用户发送的记录的逻辑验证,并发现了一些可疑的行为。在思考我是否疯了之后,我写了一个小的 PoC(概念验证)来尝试触发我发现的漏洞:一个被称为 OOB(越界)的漏洞,允许在栈内存上进行读写。

在找到过滤内核地址的方法后,控制指针就变得相当容易了。经过一些 ROP(面向返回编程) 之后,带有 root 权限的 shell 成为了现实。

4.1 根因

每当表达式的 init 例程需要解析来自用户 netlink 消息的记录时,会根据是源寄存器还是目标寄存器来调用 nft_parse_register_load 或 nft_parse_register_store 例程。我添加了一些注释:```c int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len) {

/* Given a netlink attribute and the length
 * that is required to read the requested data,
 * write a register index to `sreg` or return
 * an error on failure. */

u32 reg;
int err;


reg = nft_parse_register(attr);
err = nft_validate_register_load(reg, len);
if (err < 0)
    return err;

/* Write resulting index to the nft_expr.data structure. */
*sreg = reg;
return 0;

}


static unsigned int nft_parse_register(const struct nlattr attr) { / Convert a register to an index in nft_regs */

unsigned int reg;

/* Get specified register from netlink attribute */
reg = ntohl(nla_get_be32(attr));

switch (reg) {
/* If it's 0 to 4 inclusive,      
 * it's an OG 16-byte register and we need to
 * multiply the index by 4 (4*4=16) */
case NFT_REG_VERDICT...NFT_REG_4:
    return reg * NFT_REG_SIZE / NFT_REG32_SIZE;

/* Else we subtract 4, since we need to account
 * for the OG registers above. */
default:
    return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
}

/* So supplied values of 1, 2, 3, 4 map to
 * OG 16-byte registers, with indices 4, 8,
 * 12, 16
 * Supplied values of 5, 6, 7 overlap the verdict,
 * 8,9,10,11   overlap with OG register 1
 * 12,13,14,15 overlap with OG register 2
 * etc. */

}


static int nft_validate_register_load(enum nft_registers reg, unsigned int len) { /* We can never read from the verdict register, * so bail out if the index is 0,1,2,3 */ if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE) return -EINVAL;

/* Invalid operation, bail out */
if (len == 0)
    return -EINVAL;

/* If there would be an OOB access whenever
 * `reg` is taken as index and `len` bytes are read,
 * bail out.
 * sizeof_field(struct nft_regs, data) == 0x50 */
if (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)) 
    return -ERANGE;

return 0;

}

`*_store` 变体几乎完全相同,只是它们允许在某些条件下写入 *verbdict*。

在检查了最后的验证之后,这里确实有些不对劲:```c
if (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data))

这看起来似乎是一个 integer overflow,你们不这么认为吗?如果我们能让 reg 包含某个乘以 4 后当加上 len 时会产生 overflow 的值,我们就可以满足这些条件。在 nft_parse_register_load 中,reg 的最后一个有效字节仍然被写入指针 u8 *sreg,落入我们的 nft_expr 中,随后被用作索引。```c *sreg = reg;

¿De verdad podemos? `reg` es un `enum nft_registers` en la validación de la rutina, de todas formas. Podemos pasar valores que tengan un rango entre `0x00000001` hasta `0xfffffffb` inclusive, el rango de `nft_parse_register`; pero ¿será `reg` un valor de 32 bits en `nft_validate_register_load`? Se sabe que los compiladores podrían encoger los *enum types* si un tipo más pequeño puede representar todos los valores. Vamos a obtener una segunda opinión.

Obtenido del manual de GCC:```
The integer type compatible with each enumerated type (C90 6.5.2.2, C99 and C11 6.7.2.2).
Normally, the type is unsigned int if there are no negative values 
in the enumeration, otherwise int. If -fshort-enums is specified,
then if there are negative values it is the first 
of signed char, short and int that can represent all the values, 
otherwise it is the first of unsigned char, unsigned short and unsigned int 
that can represent all the values.

On some targets, -fshort-enums is the default; this is determined by the ABI.

TL;DR? 取决于 ABI 和可能的优化程度。我无法找到任何具体证据表明此选项在 Linux 构建中默认启用。

下载工具