Fuzz挖掘sudo提权漏洞:一次堆溢出如何逆向分析出提权思路
字数 2244 2025-08-29 22:41:10

Fuzz挖掘sudo提权漏洞:堆溢出分析与提权思路

一、文章概述

本文详细记录了从Fuzz测试发现sudo堆溢出漏洞到最终构造提权POC的全过程。重点分析了堆溢出漏洞的成因、NSS服务劫持提权原理以及利用setlocale进行堆布局的技术。通过逆向分析和动态调试,揭示了如何将CTF-Pwn中的堆利用技术应用于实际漏洞利用中。

二、sudo调研与Fuzz方式考察

sudo简介

sudo(superuser do)是Linux/Unix系统中常用的命令,允许普通用户以root权限执行命令。通过/etc/sudoers配置文件管理用户权限。

常规Fuzz的挑战

  1. 密码输入问题:sudo执行需要密码,自动化Fuzz会挂起
  2. 命令行参数处理:需要正确传递参数格式
  3. 参数控制难题:第一个参数(命令)需要精准控制
  4. 程序内部复杂性:sudo和sudoedit功能在同一程序中

AFL++命令行参数Fuzz

AFL++的argv_fuzz工具可对命令行参数进行模糊测试。使用方法:

  1. 包含头文件#include "argv-fuzz-inl.h"
  2. 在主函数开头使用宏:
    • AFL_INIT_ARGV():不保留argv[0]
    • AFL_INIT_SET0("prog_name"):保留argv[0]

三、修改sudo源码与AFL++模糊测试

源码修改关键点

  1. Patch密码验证函数:verify_user直接返回0
  2. 添加AFL++头文件并初始化参数
  3. 修改sudo_modeMODE_EDIT引导进入漏洞函数

Fuzz测试准备

  1. 准备种子样本:根据'\0'拆分参数
  2. 并行Fuzz配置:使用tmux创建多个AFL实例
  3. 编译开启Asan模式检测内存错误

四、崩溃样本分析与漏洞定位

漏洞位置

plugins/sudoers/sudoers.cset_cmnd函数,868行堆溢出

漏洞成因

  1. 函数申请0x54大小堆块
  2. 处理参数时逻辑缺陷:遇到\后非空格字符会跳过\x00检查
  3. 导致可以写入超出分配大小的数据

动态调试关键点

  1. 使用pwngdb定位溢出位置
  2. 观察NewArgv内容确定堆块大小
  3. 分析拷贝逻辑缺陷

五、可利用性评估

sudo历史提权漏洞

CVE编号 影响版本 漏洞描述 修复建议
CVE-2019-14287 <1.8.28 UID解析错误导致权限绕过 升级至1.8.28+
CVE-2021-3156 1.8.2-1.8.31p2, 1.9.0-1.9.5p1 堆缓冲区溢出 升级至1.9.5p2+
CVE-2023-22809 1.8.0-1.9.12p1 任意文件读写 升级至1.9.12p2+

堆操作跟踪

通过gdb脚本跟踪malloc/realloc/calloc/free调用,筛选出关键堆操作函数:

  1. find_editor()
  2. init_defaults()
  3. setlocale()
  4. tzset()

潜在提权手法

  1. NSS服务用户对象劫持
  2. Def_timestampdir路径竞态
  3. Userspecs对象覆盖

六、NSS劫持提权原理

NSS服务简介

Name Service Switch统一管理多种名称解析服务,通过/etc/nsswitch.conf配置查找顺序。

关键结构体

  1. service_user:具体服务配置项
  2. name_database_entry:数据库配置项
  3. name_database:顶层管理结构

提权原理

  1. 覆盖service_user->library->lib_handle为NULL
  2. 触发nss_new_service初始化
  3. 加载恶意so文件执行提权代码

堆块申请顺序

  1. service_table(0x20)
  2. name_database_entry(0x20)
  3. service_user(0x40)

七、setlocale堆布局原理

核心机制

_nl_find_locale函数根据LC_*环境变量:

  1. 申请堆块存储区域设置数据
  2. 发现无效环境变量时释放所有已申请堆块

控制规则

  1. 堆块大小:C.UTF-8@len,len=33→0x40,len=137→0xa0
  2. 释放顺序:按环境变量遍历顺序
  3. 确保构造:至少一个无效环境变量触发释放

环境变量示例

LC_CTYPE=C.UTF-8@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
LC_NUMERIC=C.UTF-8@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 
LC_TIME=C.UTF-8@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
LC_COLLATE=C.UTF-8@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
LC_IDENTIFICATION=invalid_value

八、POC构造与利用

堆布局目标

  1. 使用setlocale构造3个0x40和1个0xa0空闲堆块
  2. 让目标chunk位于溢出chunk下方
  3. 确保重要结构体不被破坏

利用步骤

  1. 构造环境变量控制堆布局
  2. 触发漏洞覆盖service_user结构
  3. 加载恶意so实现提权

关键payload

  1. 命令行参数:以\结尾的特殊构造
  2. 环境变量:控制堆布局的LC_*变量
  3. 恶意so:使用__attribute__构造函数自动执行

动态调试要点

  1. 观察tcachebin中堆块状态
  2. 确认service_user结构被覆盖
  3. 验证恶意so加载过程

总结

本漏洞利用的关键在于:

  1. 理解sudo参数处理逻辑缺陷
  2. 掌握NSS服务加载机制
  3. 熟练使用setlocale进行精确堆布局
  4. 结合堆溢出与NSS劫持实现提权

这种从Fuzz发现到完整利用的分析方法,可以应用于其他类似堆漏洞的研究与利用。

Fuzz挖掘sudo提权漏洞:堆溢出分析与提权思路 一、文章概述 本文详细记录了从Fuzz测试发现sudo堆溢出漏洞到最终构造提权POC的全过程。重点分析了堆溢出漏洞的成因、NSS服务劫持提权原理以及利用setlocale进行堆布局的技术。通过逆向分析和动态调试,揭示了如何将CTF-Pwn中的堆利用技术应用于实际漏洞利用中。 二、sudo调研与Fuzz方式考察 sudo简介 sudo(superuser do)是Linux/Unix系统中常用的命令,允许普通用户以root权限执行命令。通过/etc/sudoers配置文件管理用户权限。 常规Fuzz的挑战 密码输入问题:sudo执行需要密码,自动化Fuzz会挂起 命令行参数处理:需要正确传递参数格式 参数控制难题:第一个参数(命令)需要精准控制 程序内部复杂性:sudo和sudoedit功能在同一程序中 AFL++命令行参数Fuzz AFL++的argv_ fuzz工具可对命令行参数进行模糊测试。使用方法: 包含头文件 #include "argv-fuzz-inl.h" 在主函数开头使用宏: AFL_INIT_ARGV() :不保留argv[ 0 ] AFL_INIT_SET0("prog_name") :保留argv[ 0 ] 三、修改sudo源码与AFL++模糊测试 源码修改关键点 Patch密码验证函数: verify_user 直接返回0 添加AFL++头文件并初始化参数 修改 sudo_mode 为 MODE_EDIT 引导进入漏洞函数 Fuzz测试准备 准备种子样本:根据'\0'拆分参数 并行Fuzz配置:使用tmux创建多个AFL实例 编译开启Asan模式检测内存错误 四、崩溃样本分析与漏洞定位 漏洞位置 plugins/sudoers/sudoers.c 的 set_cmnd 函数,868行堆溢出 漏洞成因 函数申请0x54大小堆块 处理参数时逻辑缺陷:遇到 \ 后非空格字符会跳过 \x00 检查 导致可以写入超出分配大小的数据 动态调试关键点 使用pwngdb定位溢出位置 观察 NewArgv 内容确定堆块大小 分析拷贝逻辑缺陷 五、可利用性评估 sudo历史提权漏洞 | CVE编号 | 影响版本 | 漏洞描述 | 修复建议 | |---------|----------|----------|----------| | CVE-2019-14287 | <1.8.28 | UID解析错误导致权限绕过 | 升级至1.8.28+ | | CVE-2021-3156 | 1.8.2-1.8.31p2, 1.9.0-1.9.5p1 | 堆缓冲区溢出 | 升级至1.9.5p2+ | | CVE-2023-22809 | 1.8.0-1.9.12p1 | 任意文件读写 | 升级至1.9.12p2+ | 堆操作跟踪 通过gdb脚本跟踪malloc/realloc/calloc/free调用,筛选出关键堆操作函数: find_editor() init_defaults() setlocale() tzset() 潜在提权手法 NSS服务用户对象劫持 Def_ timestampdir路径竞态 Userspecs对象覆盖 六、NSS劫持提权原理 NSS服务简介 Name Service Switch统一管理多种名称解析服务,通过/etc/nsswitch.conf配置查找顺序。 关键结构体 service_user :具体服务配置项 name_database_entry :数据库配置项 name_database :顶层管理结构 提权原理 覆盖 service_user->library->lib_handle 为NULL 触发 nss_new_service 初始化 加载恶意so文件执行提权代码 堆块申请顺序 service_table (0x20) name_database_entry (0x20) service_user (0x40) 七、setlocale堆布局原理 核心机制 _nl_find_locale 函数根据LC_* 环境变量: 申请堆块存储区域设置数据 发现无效环境变量时释放所有已申请堆块 控制规则 堆块大小: C.UTF-8@len ,len=33→0x40,len=137→0xa0 释放顺序:按环境变量遍历顺序 确保构造:至少一个无效环境变量触发释放 环境变量示例 八、POC构造与利用 堆布局目标 使用setlocale构造3个0x40和1个0xa0空闲堆块 让目标chunk位于溢出chunk下方 确保重要结构体不被破坏 利用步骤 构造环境变量控制堆布局 触发漏洞覆盖 service_user 结构 加载恶意so实现提权 关键payload 命令行参数:以 \ 结尾的特殊构造 环境变量:控制堆布局的LC_* 变量 恶意so:使用 __attribute__ 构造函数自动执行 动态调试要点 观察tcachebin中堆块状态 确认 service_user 结构被覆盖 验证恶意so加载过程 总结 本漏洞利用的关键在于: 理解sudo参数处理逻辑缺陷 掌握NSS服务加载机制 熟练使用setlocale进行精确堆布局 结合堆溢出与NSS劫持实现提权 这种从Fuzz发现到完整利用的分析方法,可以应用于其他类似堆漏洞的研究与利用。