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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2022-0185-Case-Study — CVE-2022-0185的教育案例研究与漏洞利用开发指南,该漏洞是Linux内核中基于堆的缓冲区溢出,可实现本地权限提升。包含POC、QEMU调试、Ubuntu利用程序及详细技术分析。 | Kitploit
工具/GitHubGitHub/dcheng69/cve-2022-0185-case-study
权限提升漏洞分析漏洞利用CTF学习与教育二进制利用实验室与实践
GitHubdcheng69/cve-2022-0185-case-study

CVE-2022-0185-Case-Study

CVE-2022-0185的教育案例研究与漏洞利用开发指南,该漏洞是Linux内核中基于堆的缓冲区溢出,可实现本地权限提升。包含POC、QEMU调试、Ubuntu利用程序及详细技术分析。

查看仓库
31112年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2022-0185 案例研究

本案例研究是ECE 9069课程“黑客入门”的一个作业成果:https://whisperlab.org/introduction-to-hacking/

CVE-2022-0185 概述

CVE-2022-0185 是一个基于堆的缓冲区溢出漏洞,存在于 Linux 内核文件系统上下文(Filesystem Context)功能中的 legacy_parse_param 函数验证所提供的参数长度的方式中。一个非特权本地用户(如果启用了非特权用户命名空间,则不需要;否则需要命名空间的 CAP_SYS_ADMIN 权限),能够打开一个不支持文件系统上下文 API(从而回退到传统处理)的文件系统,可以利用此漏洞提升他们在系统上的权限。[1]

在该漏洞被报告后,已发布补丁修复此错误:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=722d94847de2

https://ubuntu.com/security/CVE-2022-0185#impact-score

发现者提供了一份详细的写入文章:https://www.hackthebox.com/blog/CVE-2022-0185:_A_case_study

在本仓库中,我将解释重现此漏洞的基本步骤和相关背景信息。此外,如果你有任何不清楚的地方,可以给我发邮件:[email protected],我很乐意回答问题。

1. 引言

CVE-2022-0185 漏洞于 2022 年 2 月 11 日发布,CVSS 3.x 基础评分为 8.4(高)。[1] 该漏洞是一个基于堆的缓冲区溢出,由无符号整数下溢引起。

该漏洞在 Linux v5.1 内核中引入,影响所有内核版本高于 5.1 的 Linux 发行版。例如,Ubuntu 20.04 LTS (focal) 曾受此漏洞影响。然而,补丁已发布,自版本 5.4.0-96.109 起可用。[3]

利用此漏洞允许非特权本地用户提升其在系统上的权限,可能危及整个系统。[1][2] 以下是 CVSS 评分的详细分析: 基础评分:8.4,表示需要立即关注的重要安全风险。 影响评分:5.9,表明如果被利用,可能造成实质性损害。高机密性、完整性和可用性值促成了这一评分。 可利用性评分:2.5,表明相对较高的可利用性。本地攻击向量、高完整性和高可用性值促成了这一评分。

表 1.1 和表 1.2 提供了关于这些评分及其组件的更多信息。

CVSS v3.1 严重性值
基础评分8.4 高
影响评分5.9
可利用性评分2.5

表 1.1 CVSS 严重性评分[1]

CVSS v3.1 指标值
攻击向量 (AV)本地
所需权限 (PR)无
用户交互 (UI)无
机密性 (C)高
完整性 (I)高
可用性 (A)高

表 1.2 CVSS 向量[1]

2. 背景和相关概念

2.1 无符号数下溢

2.1.1 二进制补码

现代计算机中有两种整数类型:有符号和无符号。有符号数的表示通常涉及一种称为二进制补码的运算。[4] “二进制补码使用具有最高位值的二进制数字作为符号,指示二进制数是正还是负。”[4]

引入二进制补码会将减法计算转化为加法,从而简化 CPU 的设计和实现。生成一个整数的二进制补码涉及三个步骤:[4]

  • 步骤 1:“从数字的二进制表示开始,最高位为符号位”;
  • 步骤 2:“将所有位取反”;
  • 步骤 3:“对取反后的整个数字加 1,忽略任何溢出。”

图 2.1.1.1 以图表形式展示了转换过程,并附有一个实际示例,将“-6”转换为其二进制补码格式。

twos_complement.drawio

图 2.1.1.1 将“-6”转换为二进制补码

图 2.1.1.2 展示了将“-6”的二进制补码与“+6”相加的过程。这说明了如何使用二进制补码将加法作为减法的替代。

twos_complement-Page-2.drawio

图 2.1.1.2 使用二进制补码进行加法

2.1.2 RAM 中的数字表示

从第 2.1.1 节中,我们已经理解了什么是二进制补码。现在,让我们看看计算机中无符号数下溢的场景。在现代计算机中,使用无符号数时,最高有效位不被视为符号位;相反,它是无符号数本身的一部分。这意味着在使用无符号数执行减法时必须谨慎,因为它可能导致一种称为无符号数下溢的情况。[5]

图 2.1.2.1 展示了对于 8 位无符号数,从 5 中减去 6 的情况。由于无符号数回绕,最终结果是 255。当下溢发生在条件语句中时,有可能破坏该语句的功能。

twos_complement-Page-3.drawio

图 2.1.2.1 无符号数下溢

2.2 Linux 内核内存

2.2.1 堆内存中的 Slab

在 Linux 内核中,Slab 分配器是一种内存管理机制,用于高效分配和释放小块内存。它通过维护多个 Slab 缓存来提供性能,每个缓存包含固定大小的内存块。通常,kmalloc-32 分配 32 字节内存,它是一个 kmalloc-32 slab;而 kmalloc-4k 分配 4096 字节内存,它是一个 kmalloc-4k slab。[6]

此外,Linux 内核中的 slab 分配通常涉及从内核堆内存区域内的连续地址空间分配内存。该连续地址由内核管理,用于分配各种内核对象和数据结构。图 2.2.1.1 显示了 Linux 内核内存中 slab 的布局。

img

图 2.2.1.1 Linux 中的 Slab 分配器 [7] (该图的作者是 https://leviathan.vip/)

3 漏洞技术分析

3.1 概念验证

如果你想使用自行编译的 Linux 内核重现该过程,请阅读以下 Markdown 文件以获取背景信息:

  1. 首先阅读关于如何编译 Linux 内核的 Markdown:https://github.com/dcheng69/CVE-2022-0185-Case-Study/blob/main/compile_linux/compile.md
  2. 然后阅读关于如何准备一个 RAM 文件系统以执行 POC 的 Markdown:https://github.com/dcheng69/CVE-2022-0185-Case-Study/blob/main/ramfs/ramfs.md
  3. 最后遵循 poc markdown:https://github.com/dcheng69/CVE-2022-0185-Case-Study/blob/main/Poc/poc.md

注意:

所有 Markdown 文件以及代码和脚本均位于此仓库的不同文件夹中,每个文件夹都附带自己的 Markdown 文件,在进行任何操作前请先阅读它!

3.1.1 内核中的无符号下溢

在第 2.1 节中,我们解释了无符号下溢的工作原理。现在,我们将检查包含此漏洞的内核函数。

用户“clubby789”在内核函数 legacy_parse_param 中发现了一个漏洞。该函数主要负责解析传递给内核的参数。在 CVE-2022-0185 中,该函数在通过 fsopen 打开文件描述符后,再使用 fsconfig 函数将配置键值对传递给内核时被调用。legacy_parse_param 的简化版本如以下代码所示。[2]```c static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) { struct legacy_fs_context *ctx = fc->fs_private; // [1] unsigned int size = ctx->data_size; // [2] size_t len = 0; int ret; [ ... ] switch (param->type) { case fs_value_is_string: len = 1 + param->size; // [3] case fs_value_is_flag: len += strlen(param->key); break; default: return invalf(fc, "VFS: Legacy: Parameter type for '%s' not supported", param->key); } if (len > PAGE_SIZE-2-size) return invalf(fc, "VFS: Legacy: Cumulative options too large"); // [4] [ ... ] if (!ctx->legacy_data) { ctx->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL); // [5] if (!ctx->legacy_data) return -ENOMEM; } ctx->legacy_data[size++] = ','; // [6] len = strlen(param->key); memcpy(ctx->legacy_data + size, param->key, len); size += len; if (param->type == fs_value_is_string) { ctx->legacy_data[size++] = '='; memcpy(ctx->legacy_data + size, param->string, param->size); size += param->size; } ctx->legacy_data[size] = '\0'; ctx->data_size = size; ctx->param_type = LEGACY_FS_INDIVIDUAL_PARAMS; return 0; }

从上述代码片段可以看出,第[1]行和第[2]行设置了代码的上下文,而第[4]行包含了发生无符号下溢的语句。第[5]行处理堆slab分配,第[6]行和第[7]行负责将数据填充到分配的slab中。值得注意的是,第[6]行添加了一个逗号(',')作为单独的分隔符,并且还添加了一个等号('='),导致实际数据大小之外多了两个字节。

在第[4]行中,if语句内的变量包含PAGE_SIZE(设置为4096的宏)和size(一个无符号64位数)。当无符号数累计到4095时,会发生下溢,导致if语句始终评估为假。这允许对相邻slab进行越界写入。下溢是由于减去一个无符号数引起的,结果是4096 - 4095,得到一个无符号数18446744073709551615。[2]

`18446744073709551615`这个结果在以下文本中进行解释:```c
if (len > PAGE_SIZE-2-size) return invalf(fc, "VFS: Legacy: Cumulative options too large");

注意,这里 PAGE_SIZE 等于 4096 字节,而 2 对应添加的字符 , 和 =,用于分隔每个 key-value 对。问题在于 size 是一个 无符号值,因此当 size 达到 4095 时,表达式 PAGE_SIZE-2-size 将等于 有符号值: -1,但作为 无符号值: 18446744073709551615,这是由于二进制补码表示,如下图所示![3]

poc.drawio

因此上述 if 语句始终为 false,这意味着剩余的数据会复制到我们分配的 slab 之外的堆区域!

3.1.2 POC 代码分析

在理解了无符号数下溢如何发生后,我们可以继续构建一个概念验证(POC)代码来演示该漏洞。

用户 “clubby789” 提供了一个详细的 POC 代码,如下代码片段所示。代码简洁明了;它首先打开一个名为 ext4 的文件描述符,然后多次使用 fsconfig 向内核填充数据。

这里需要注意两点:

  • 调用 fsopen 打开 ext4 需要 CAP_SYS_ADMIN 权限。因此,在后续的漏洞利用中,我们将使用 unshare 获得这些权限。但在这个概念验证(POC)中,我们仅以 root 权限运行程序。
  • 每个我们填充到内核的键值对长度为 33。但函数 legacy_parse_param 会在开头插入一个逗号 (','),并在键和值之间插入一个等号 ('=')。因此,每个周期实际占用的大小为 35。```c #define _GNU_SOURCE #include <sys/syscall.h> #include <stdio.h> #include <stdlib.h> #ifndef __NR_fsconfig #define __NR_fsconfig 431 #endif #ifndef __NR_fsopen #define __NR_fsopen 430 #endif #define FSCONFIG_SET_STRING 1 #define fsopen(name, flags) syscall(__NR_fsopen, name, flags) #define fsconfig(fd, cmd, key, value, aux) syscall(__NR_fsconfig, fd, cmd, key, value, aux) int main(void) { char* key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // 33 characters [1] int fd = 0; fd = fsopen("ext4", 0); // [2] if (fd < 0) { puts("Open failed!\n"); exit(-1); } for (int i = 0; i < 130; i++) { fsconfig(fd, FSCONFIG_SET_STRING, "\x00", key, 0); //[3] } return 0; }
在3.1.1节中,我们提到在观察到越界写入之前,必须填充4095字节的数据。考虑到每个周期仅填充35字节,我们需要执行操作117次(4095 / 35),然后才能观察堆内存以完成概念验证。

### 3.1.3 使用QEMU的POC

在本仓库:GitHub - dcheng69/CVE-2022-0185-Case-Study 中,我们提供了一个名为poc.sh的shell脚本,以方便调试过程。在开始设置之前,请阅读Poc文件夹中的markdown文件。

由于legacy_parse_param是一个内核函数,您需要调试内核函数。为此,您需要编译内核源码以获得必要的符号和源码。我们还提供了一个详细的markdown文件来指导您完成此过程。请参阅Compile_linux文件夹以了解更多细节。

在图3.1.3.1中,我们演示了在向内核堆填充4095字节数据后,通过利用无符号下溢成功触发了越界写入。此外,我们总共向一个kmalloc-4k slab填充了4130字节数据,成功破坏了相邻的slab。虽然在这个例子中相邻slab不包含任何信息(全为零),但我们可以精心构造代码来利用此特性写入恶意数据。我们将在3.2 利用部分演示如何实现这一点。

![图3.1.3.1 使用QEMU的POC](https://assets.kitploit.com/production/public/readmes/24547/2a679fe489b44c6f1f46040bf6c74c7481d6bd4eaf70a594830cc4c0afc2ffc4.png)

**图 3.1.3.1 使用QEMU的POC**

更多详情请参阅本仓库`Poc`文件夹下的 https://github.com/dcheng69/CVE-2022-0185-Case-Study/blob/main/Poc/poc.md!

## 3.2 利用
下载工具