使用Qiling分析Dlink DIR-645中的缓冲区溢出(part I)
字数 1555 2025-08-22 12:22:48
Dlink DIR-645 缓冲区溢出漏洞分析教学文档 (Part I)
1. 漏洞概述
本教学文档将详细分析 Dlink DIR-645 路由器中的缓冲区溢出漏洞 (CVE-2013-7389),该漏洞存在于 hedwig.cgi 程序中,影响多个 Dlink 路由器型号。
漏洞基本信息
- 漏洞编号: CVE-2013-7389
- 影响组件:
hedwig.cgi(实际为cgibin二进制文件) - 漏洞类型: 缓冲区溢出
- 攻击向量: 通过特制的 HTTP Cookie 头触发
2. 环境准备
2.1 所需工具
- Qiling 框架 (用于模拟执行和漏洞分析)
- Ghidra (反汇编和逆向工程)
- binwalk (固件提取)
- gdb-multiarch (调试)
2.2 固件获取与提取
- 下载存在漏洞的固件版本
- 使用 binwalk 提取固件:
binwalk -e DIR645A1_FW103RUB08.bin
3. 漏洞定位
3.1 关键文件定位
hedwig.cgi是一个符号链接,指向/htdocs/cgibin二进制文件- 实际漏洞存在于
cgibin程序中
3.2 关键函数定位
- 使用 Ghidra 分析
cgibin二进制文件 - 搜索字符串 "hedwig.cgi" 找到相关函数
- 定位到
hedwigcgi_main函数
4. 漏洞触发条件分析
4.1 必要条件
- HTTP 请求方法必须为 POST
- HTTP Cookie 头必须包含
uid=参数 uid=参数值需要足够长以触发溢出
4.2 环境变量设置
使用 Qiling 框架时需要设置的环境变量:
required_env = {
"REQUEST_METHOD": "POST",
"HTTP_COOKIE": "uid=" + "A"*1041 + "1111"
}
5. 漏洞详细分析
5.1 漏洞触发流程
- 程序检查
REQUEST_METHOD环境变量是否为 "POST" - 从
HTTP_COOKIE环境变量中提取uid=参数值 - 使用
strcpy将uid=值复制到堆缓冲区 - 使用
sprintf格式化字符串,将堆缓冲区内容复制到栈变量 - 过长的输入导致栈上保存的寄存器值被覆盖
5.2 关键代码片段
// 从环境变量获取 Cookie
cookie_value = getenv("HTTP_COOKIE");
// 查找 uid= 参数
// 使用 strcpy 复制到堆缓冲区
strcpy(heap_buffer, uid_value);
// 使用 sprintf 格式化字符串到栈缓冲区
sprintf(stack_buffer, "%s/%s/postxml", "/runtime/session", heap_buffer);
5.3 内存布局
- 堆缓冲区地址: 0x437898
- 栈缓冲区地址: 0x7ff3c1e0
- 关键寄存器覆盖:
$ra(返回地址) 被覆盖为 0x41414141- 多个保存的寄存器 (
$s0-$s8) 被覆盖
6. 动态分析技术
6.1 使用 Qiling 进行模拟
from qiling import *
def redirect_to_hedwigcgi_main(ql):
ql.reg.arch_pc = HEDWIGCGI_MAIN # 重定向到目标函数
ql = Qiling(path, rootfs, output="debug", env=required_env)
ql.hook_address(redirect_to_hedwigcgi_main, MAIN_ADDR)
6.2 函数 Hook 技术
def hook_sess_get_uid(ql):
ql.hook_code(print_asm) # 打印执行的汇编指令
ql.hook_address(hook_sess_get_uid, SESS_GET_UID)
6.3 关键函数 Hook
def strcpy_hook(ql):
print("dst:", hex(ql.os.function_arg[0]))
print("src:", ql.mem.string(ql.os.function_arg[1]))
return 2
ql.set_api('strcpy', strcpy_hook, QL_INTERCEPT.ENTER)
7. 调试技巧
7.1 启用调试模式
ql.debugger = True # 启用GDB调试服务器
7.2 GDB 连接
gdb-multiarch
(gdb) set remotetimeout 100
(gdb) target remote 127.0.0.1:9999
7.3 指令级跟踪
def print_asm(ql, address, size):
buf = ql.mem.read(address, size)
for i in md.disasm(buf, address):
print(f":: {hex(i.address)}: {i.mnemonic} {i.op_str}")
8. 漏洞验证
8.1 崩溃验证
发送特制的 HTTP 请求:
POST /hedwig.cgi
Cookie: uid=AAAAAAAA...[1041 bytes]...1111
8.2 崩溃现象
- 程序计数器 PC 被覆盖为 0x41414140
- 多个寄存器被覆盖为 0x41414141
- 错误信息:
Invalid memory read (UC_ERR_READ_UNMAPPED)
9. 漏洞总结
9.1 根本原因
- 不安全的
strcpy使用导致堆缓冲区溢出 - 后续的
sprintf将超长数据复制到栈缓冲区 - 缺乏输入长度检查导致关键寄存器被覆盖
9.2 利用思路
- 控制
uid=参数的内容 - 精心构造溢出数据覆盖返回地址
- 实现任意代码执行
10. 后续步骤
在 Part II 中,我们将:
- 精确计算偏移量
- 构造 ROP 链
- 开发完整的漏洞利用代码
- 实现远程代码执行
11. 参考资料
本教学文档详细介绍了 Dlink DIR-645 缓冲区溢出漏洞的分析过程,包括环境准备、漏洞定位、动态分析和调试技巧。在 Part II 中,我们将深入探讨如何利用此漏洞实现代码执行。