Sudo/sudo_debug格式化字符串漏洞

实验目的

学习目标

​ 学习漏洞的基本原理,通过对漏洞的复现,了解格式化字符串漏洞的成因和利用方式,特别是在关键系统组件Sudo中的表现形式。掌握如何定位代码中的漏洞点,如何通过恶意输入触发漏洞,并通过调试工具验证利用的有效性。提升安全防护意识通过实验,认识格式化字符串漏洞对系统安全的威胁,理解其可能的攻击后果。熟悉漏洞修复过程,并验证修复措施的有效性。模拟实际攻击过程中可能的条件和操作步骤,为后续在生产环境中发现和应对类似漏洞积累经验。提高综合能力,通过实验提升在漏洞复现中的综合能力,包括环境搭建、源码调试、漏洞利用和修复验证等环节的实践技巧。

复现目标

​ 通过输入恶意格式化字符串代码,致使存在漏洞版本的sudo触发漏洞,泄露敏感信息,泄露内存地址,进而实现任意代码执行等非法操作。

实验环境

操作机/靶机:VMware Ubuntu64位
虚拟机系统及版本:Ubuntu 12.04
sudo版本:sudo1.8.3p1
使用工具:终端、代码编辑器
本机配置:win11家庭版 16G主存 

实验原理

环境变量、调试模式及源码引入

​ CVE-2012-0809 是一种由格式化字符串引发的漏洞,SUDO_DEBUG 是触发漏洞的关键条件之一:当 Sudo 启用了 —enable-debug 编译选项时,SUDO_DEBUG 环境变量的值会被 sudo_debug 函数解析。漏洞的核心问题是,sudo_debug 函数未安全地处理 SUDO_DEBUG 的输入,导致格式化字符串攻击的发生。

​ 此外,sudo_debug函数的源代码也值得研究。以下代码取自受漏洞影响的 Sudo 版本:

1
2
3
4
5
6
7
8
9
void sudo_debug(int level, const char *fmt, ...) {
va_list ap;

if (sudo_debug_enabled) {
va_start(ap, fmt);
vfprintf(sudo_debug_file, fmt, ap); // 漏洞点:未验证格式化字符串
va_end(ap);
}
}

源码漏洞点分析

​ 函数参数 fmt 是一个格式化字符串,用于定义 vfprintf 的格式。问题在于:fmt 是直接从用户输入传入的(例如通过环境变量 SUDO_DEBUG),没有进行任何校验。攻击者可以通过在 SUDO_DEBUG 的值中插入格式化字符串(如 %n),强制 vfprintf 执行意外操作,写入指定内存地址。vfprintf 是一个变参函数,支持多种格式化指令(如 %s、%d、%n)。%n 指令会将已写入字符的数量存储到一个指定的内存地址。如果攻击者构造恶意的 fmt 参数,可能导致:写入任意内存地址、修改关键变量(如函数指针)、进一步实现权限提升或代码执行。

​ 漏洞触发条件:Sudo 被编译时启用了 —enable-debug,环境变量 SUDO_DEBUG 被设置为恶意值。

漏洞核心问题

缺乏输入验证:

​ fmt 参数的来源是环境变量 SUDO_DEBUG,属于外部输入,但代码未对其进行过滤或校验。
理想情况下,fmt 应是开发者定义的固定格式字符串,而不是用户输入。

错误的日志处理设计:

​ 调试日志的实现直接依赖于外部可控的字符串,未遵循安全的日志记录实践。
函数滥用:

​ 使用了变参函数(如 vfprintf)处理未可信的输入,进一步增加了攻击面。

修复方式

​ 在后续版本中(如 Sudo 1.8.3p2),开发者修复了该漏洞,通过改进 sudo_debug 函数的实现,避免格式化字符串漏洞:

固定格式字符串:

​ 确保格式化字符串是固定值,而非用户可控输入:

1
fprintf(sudo_debug_file, "%s", user_input);  // 安全的日志记录

严格校验:

​ 对环境变量 SUDO_DEBUG 的值进行解析和验证,防止恶意内容的注入。
替换不安全函数:

​ 使用安全函数(如 snprintf)替换容易产生漏洞的变参函数。

攻击方式

​ 攻击者可通过精心构造的格式化字符串,利用 %n /%x/%s等格式化字符串指令构造恶意代码触发漏洞,泄露敏感信息,向指定内存地址写入数据,进而实现任意代码执行等非法操作。

实验步骤

1. Ubuntu旧版本下载以及sudo版本回滚

​ 通过查阅资料可知,CVE-2012-0809漏洞高概率存在于Ubuntu10.04~12.04版本中。若Ubuntu版本过高,则会降低对sudo版本的兼容性。在互联网上寻找Ubuntu12.04 ISO镜像文件下载:

1

​ 具体安装过程以及系统初始化设置此处略过。

​ 打开刚刚装好的Ubuntu12.04终端,输入命令wget https://www.sudo.ws/dist/sudo-1.8.3p1.tar.gz下载旧版本的sudo1.8.3p1安装包:

屏幕截图 2024-11-26 143808

​ 解压并进入目录:

屏幕截图 2024-11-26 150747

屏幕截图 2024-11-26 145747

​ 配置并安装旧版本sudo:

屏幕截图 2024-11-26 152502

​ 查看sudo版本,安装成功:

屏幕截图 2024-11-26 152633

2. 恶意代码编写及测试

​ 通过编写容易触发漏洞的恶意代码,向sudo_debug函数传递字符和格式化字符串,进而触发当前sudo版本的漏洞。具体原理上述已经提及,此处不再赘述。

​ 恶意代码如下:

屏幕截图 2024-11-26 153530

​ 赋予其可执行权限:

屏幕截图 2024-11-26 153729

​ 此时,由于我们还没有编辑sudo_debug环境变量以及调试功能,故上述代码运行结果应该为正常结果:

屏幕截图 2024-11-26 183131

3. 配置环境变量、打开调试模式

​ 查阅案例资料可知,sudo漏洞触发于打开调试模式之后。当 Sudo 启用了 —enable-debug 编译选项时,SUDO_DEBUG 环境变量的值会被 sudo_debug 函数解析。漏洞的核心问题是,sudo_debug 函数未安全地处理 SUDO_DEBUG 的输入,导致格式化字符串攻击的发生。首先,我们将SUDO_DEBUG环境变量设置为1~9之间任意一个数,此处我将其设置为9以获得最详细的调试信息:

屏幕截图 2024-11-26 193220

​ 同时,我们需要使用visudo,修改sudoer文件的内容,添加一行Default debug以辅助开启调试模式:

屏幕截图 2024-11-26 194213

屏幕截图 2024-11-26 194330

4. 漏洞复现及分析

​ 经过第3步的操作后,sudo_debug调试模式已被打开,可以向该函数传入格式化字符串并触发漏洞。此时我们再次运行test.sh文件,结果如下:

屏幕截图 2024-11-26 194749

​ 分析可知,除了打印出AAAA原本应该打印出的内容,第5~第12字符还打印出了字符‘A’的16进制ASCII码,第13~第28字符打印出了内存地址,后4字节则为栈上内容。

5. 漏洞修复

​ 要想修复上述漏洞,可以下载并安装修复版本的sudo(1.8.3p2及以上),这个方法当然也是最有效的,此处不再演示此方法。

​ 此处我们使用的方法是删除sudoer.tmp文件中添加的代码并将环境变量SUDO_DEBUG的值置null:

屏幕截图 2024-11-26 195651

再次运行test.sh文件,输出结果回到正常:

屏幕截图 2024-11-26 195852

实验总结

​ 通过实验,我们成功复现了 sudo_debug 函数的格式化字符串漏洞,证明了攻击者可以利用该漏洞执行任意代码。实验显示,该漏洞仅在 Sudo 启用调试模式的情况下可被利用。

​ 格式化字符串漏洞虽然常见,但在关键组件 Sudo中,影响尤其严重。此类漏洞的防范主要依赖于严格的输入验证和正确的格式化字符串使用方法。

​ 修复建议:使用安全的字符串函数(如 snprintf)代替不安全的函数、在调试信息中避免直接处理用户输入、及时升级到修复该漏洞的 Sudo 版本(如 1.8.3p2)。