Files
Penetration_Testing_POC/iot/TOTOLINK CVE-2022-26210 漏洞分析&复现.md

193 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TOTOLINK CVE-2022-26210 漏洞分析&复现
> 0431物联卫士实验室
> 来源:https://www.iotsec-zone.com/article/340
## 漏洞描述
> Totolink A830R V5.9c.4729_B20191112, A3100R V4.1.2cu.5050_B20200504, A950RG V4.1.2cu.5161_B20200903, A800R V4.1.2cu.5137_B20200730, A3000RU V5.9c.5185_B20201128, and A810R V4.1.2cu.5182_B20201026 were discovered to contain a command injection vulnerability in the function setUpgradeFW, via the FileName parameter. This vulnerability allows attackers to execute arbitrary commands via a crafted request.
> ——NIST
> Totolink多款路由器产品的setUpgradeFW函数的FileName参数存在命令注入漏洞。攻击者可利用该漏洞执行任意命令。
> ——阿里云漏洞库
## 固件模拟
这个漏洞影响范围还挺广的,上报的就有6个型号,那么大概率还有其他型号也有类似漏洞。
所以咱们就以`A3000RU V5.9c.5185_B20201128`为例,去模拟真实环境。(其实是正好这个型号的这个版本之前已经模拟成功过 {{doge}} )
至于模拟的话呢,这个型号其实挺好模拟的,直接`FirmAE`一键跑就能起来。不过因为系统是`Linux`的,`TOTOLINK`的憨批前端直接给当成移动端UA特征去显示了。
![0.png](https://www.iotsec-zone.com/images/MdImg/75994c2fa2f46d1fa06d11146986bf92.png)
不过跑起来大概一分钟左右,就会莫名其妙访问不了。
去看了系统日志,发现有个`reboot`调用,好家伙,居然是`watchdog`调的。
![1.png](https://www.iotsec-zone.com/images/MdImg/825f7e660a4085e6f2bfcd9de9501fd3.png)
那么就想办法改改`watchdog`呗,主要两个思路:
1. 直接干掉`watchdog`
2. 改掉重启的前置条件
第一个思路的话,可能有些路由器干掉`watchdog`就会崩,所以算是简单粗暴的下策,但是这回咱们测了下(启动后马上手动`kill -9`掉)发现直接干掉问题不大,没啥负面影响,所以干就完了。
咱们把`watchdog`重命名成`watchdog_`,然后写个`shell`当成假的`watchdog`。
![2.png](https://www.iotsec-zone.com/images/MdImg/aa55152e9508056d93f2ee23c7e16154.png)
![3.png](https://www.iotsec-zone.com/images/MdImg/b8ce464b049d384afa18ab8908e83973.png)
然后重新模拟一遍,成功干掉。
![4.png](https://www.iotsec-zone.com/images/MdImg/bd89a7f27795b4626ae142c62d7ff5da.png)
## 漏洞复现
先默认账号密码`admin@admin`登录,根据漏洞描述的点位`setUpgradeFW`推测,应该是固件升级的接口,尝试抓包。
![5.png](https://www.iotsec-zone.com/images/MdImg/96ef6bd1e4e512da8819232733d3d8f8.png)
![6.png](https://www.iotsec-zone.com/images/MdImg/366a2699b9df1d551219cb1461c610c1.png)
似乎和描述的`setUpgradeFW`和`FileName`参数不太一样。
尝试直接全局搜索关键词,发现`cstecgi.cgi`确实是相关的,以及一个`cstecgi.cgi`。
那先看看这个cgi的代码吧。
![7.png](https://www.iotsec-zone.com/images/MdImg/d471396e1b1b59075b020327c7c93dc2.png)
这处确实和漏洞描述高度相关,那往上找找前置条件的`v28`是啥吧,看起来是`get`传参,因为上面咱们抓包的时候就见过一模一样的`get`传参。
![8.png](https://www.iotsec-zone.com/images/MdImg/f4f910551ef40a4f73f7d5825063a4cc.png)
是从`QUERY_STRING`拿的,那就没猜错,确实就是`get`传参的。
那么回头看看填充`FileName`的`v20`是啥。
![9.png](https://www.iotsec-zone.com/images/MdImg/a15b4c74b413d02ed400b0ed29b4e697.png)
发现是`UPLOAD_FILENAME`,这应该是上传的文件名?所以尝试改一下数据包重新发送。
![10.png](https://www.iotsec-zone.com/images/MdImg/c5929714f7ac94d2a583d94d834c331b.png)
但是命令执行没成功。
![11.png](https://www.iotsec-zone.com/images/MdImg/232dc6a4260092ae49e5440dbb25e60e.png)
那就继续往下找找执行点在哪吧。
![12.png](https://www.iotsec-zone.com/images/MdImg/6bce302921bb6b75fbb9ff5c9e2c91c4.png)
但是没找到,因为再往下走就开始准备响应了,所以最有可能的就是`web_getData`这个调用了,因为只有这个的传参是和请求细节高度相关的。
看看这个调用,发现是个动态库的接口。
![13.png](https://www.iotsec-zone.com/images/MdImg/aefb1360b152553c653d502c370121e7.png)
全局搜一下,发现这个调用来源是`libmosquitto.so`。
![14.png](https://www.iotsec-zone.com/images/MdImg/e9eaa827fa3e2bda4fee06beb90bd59b.png)
但这不是`MQTT`嘛,难道这路由器的内部数据流动是靠的`MQTT`推送?
搜一下调用看看。
![15.png](https://www.iotsec-zone.com/images/MdImg/afaffe45336600d25ea16f487d973a32.png)
发现刚才那个`upgrade.so`库也调用过这个`MQTT`库。
那会不会这个`upgrade.so`库就是负责处理这个固件升级消息的呢?
结果发现这里面真有一个`setUpgradeFW`函数。
![16.png](https://www.iotsec-zone.com/images/MdImg/b37270a76677990c3ea4747c8d11773a.png)
然后最关键的数据`FileName`是给了`v8`,然后`v8`出现的地方就这么三处。
![17.png](https://www.iotsec-zone.com/images/MdImg/0e0e4daed1609ee2ec1871ceeeafec38.png)
![18.png](https://www.iotsec-zone.com/images/MdImg/68b4ba3f69b93efb49b1fdde56c501fe.png)
![19.png](https://www.iotsec-zone.com/images/MdImg/7cc28e94664858688258d38d47b408ac.png)
下面两处本质上是一样的,只不过前置条件不同而已,而且这里确实是直接把`v8`填充进命令就去`system`执行了。
那不用多说,肯定是最后这个最好触发嘛,毕竟在逻辑最外层,只需要固件小于`1000`字节就行。
而且还不需要走里面的固件格式检查,相当于说咱们之前抓包用的那个数据包就符合要求。
但这就奇怪了,如果是把`` `pwd>/web_cste/1.txt` ``填充进`rm -f %s 1>/dev/null 2>&1`的话,那就是``rm -f `pwd>/web_cste/1.txt` 1>/dev/null 2>&1``,这很明显是可以执行的嘛。
那么问题出在哪呢?难道是文件名被编码了?
尝试修改命令内容,直接输出文件名看看到底啥情况。
![20.png](https://www.iotsec-zone.com/images/MdImg/f9c42510b90c5f147c19429dfbd12e39.png)
但是这个动态库是谁调的咱还不知道,去全局搜一下。
![21.png](https://www.iotsec-zone.com/images/MdImg/147820be9a3fa34695e9321acbabd6bf.png)
完全没有啊,难道是以文件夹为单位的动态加载?
![22.png](https://www.iotsec-zone.com/images/MdImg/d9a48ed96e3b59f911711ae732175183.png)
发现有个`cste_sub`可执行文件,看看有没有这个进程,毕竟如果数据走`MQTT`的话,肯定是有后台进程的嘛。
![20.png](https://www.iotsec-zone.com/images/MdImg/09b8a10f48bedee31054337c4ea6efe6.png)
还真有一个,那就替换一下刚才改的动态库,然后kill掉这个进程自己前台跑一下,看看有没有输出。
![21.png](https://www.iotsec-zone.com/images/MdImg/b83f6f149b515bbda021a2f87f3c6e6a.png)
结果发现文件名是`/var/uImage.img`?
看看内容是啥。
![22.png](https://www.iotsec-zone.com/images/MdImg/292eadcd5196323a9ecce7287011bb4e.png)
这不是整个请求体嘛...为啥没解析就全存了,难道这个功能本来就有问题?
全局搜一下看看`uImage`这文件名是哪来的。
![23.png](https://www.iotsec-zone.com/images/MdImg/a393bf629773e58fbfd6b5c3c77fcbca.png)
![24.png](https://www.iotsec-zone.com/images/MdImg/775aa0e813da32a6e99388462d087f9f.png)
![25.png](https://www.iotsec-zone.com/images/MdImg/fbb676abc3c0401e1026bb8bad1112f0.png)
这...上传的文件名是写死的固定值,那咋实现可控的?
回头看看`cgi`,发现了个小细节。
![26.png](https://www.iotsec-zone.com/images/MdImg/38a6417f52d5c0bc512001dc0b940661.png)
![27.png](https://www.iotsec-zone.com/images/MdImg/1efbdaf44ba8f5401e0f414d2ee3c847.png)
如果`get`传参为空的话,最后会落到`124`行的`v3 = cJSON_Parse(v29);`,那么基于`get`传参为空这个前提,`v29`是否可控呢?
![28.png](https://www.iotsec-zone.com/images/MdImg/22b7c517f3a4d794c51dc1112f3cd04d.png)
似乎确实可以,这个`stdin`应该就是请求体,因为上面`CONTENT_LENGTH`正好作为`read`的长度用了。
那么就想想怎么构造请求呗,既然`v29`完全是可以自己传的,那咱们只要把上面`{"topicurl":"setting/setUpgradeFW","FileName":"%s","Flags":"%d","ContentLength":"%s"}`填充一下然后通过`post`传进去好像就行?
![29.png](https://www.iotsec-zone.com/images/MdImg/1fcd78215d5b4685fef156e08d265693.png)
看看输出的文件名,确实过来了。
![30.png](https://www.iotsec-zone.com/images/MdImg/3b9a4e8c83deb23b94d4a2bb6445e2cd.png)
那咱们把刚才那个动态库给替换回原来的(不然命令注入点没了),然后重新跑一下看看。
![31.png](https://www.iotsec-zone.com/images/MdImg/909bd40c6e50da921285f8ff94583dac.png)
成功执行了`pwd`,再看看别的命令,比如说`ls -al /`。
![32.png](https://www.iotsec-zone.com/images/MdImg/dfab80c4397549aad0f0463ea472b65a.png)
漏洞成功复现。