mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2026-10-24 08:54:28 +00:00
193 lines
8.8 KiB
Markdown
193 lines
8.8 KiB
Markdown
# 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特征去显示了。
|
||
|
||

|
||
|
||
不过跑起来大概一分钟左右,就会莫名其妙访问不了。
|
||
|
||
去看了系统日志,发现有个`reboot`调用,好家伙,居然是`watchdog`调的。
|
||
|
||

|
||
|
||
那么就想办法改改`watchdog`呗,主要两个思路:
|
||
|
||
1. 直接干掉`watchdog`
|
||
2. 改掉重启的前置条件
|
||
|
||
第一个思路的话,可能有些路由器干掉`watchdog`就会崩,所以算是简单粗暴的下策,但是这回咱们测了下(启动后马上手动`kill -9`掉)发现直接干掉问题不大,没啥负面影响,所以干就完了。
|
||
|
||
咱们把`watchdog`重命名成`watchdog_`,然后写个`shell`当成假的`watchdog`。
|
||
|
||

|
||
|
||

|
||
|
||
然后重新模拟一遍,成功干掉。
|
||
|
||

|
||
|
||
## 漏洞复现
|
||
|
||
先默认账号密码`admin@admin`登录,根据漏洞描述的点位`setUpgradeFW`推测,应该是固件升级的接口,尝试抓包。
|
||
|
||

|
||
|
||

|
||
|
||
似乎和描述的`setUpgradeFW`和`FileName`参数不太一样。
|
||
|
||
尝试直接全局搜索关键词,发现`cstecgi.cgi`确实是相关的,以及一个`cstecgi.cgi`。
|
||
|
||
那先看看这个cgi的代码吧。
|
||
|
||

|
||
|
||
这处确实和漏洞描述高度相关,那往上找找前置条件的`v28`是啥吧,看起来是`get`传参,因为上面咱们抓包的时候就见过一模一样的`get`传参。
|
||
|
||

|
||
|
||
是从`QUERY_STRING`拿的,那就没猜错,确实就是`get`传参的。
|
||
|
||
那么回头看看填充`FileName`的`v20`是啥。
|
||
|
||

|
||
|
||
发现是`UPLOAD_FILENAME`,这应该是上传的文件名?所以尝试改一下数据包重新发送。
|
||
|
||

|
||
|
||
但是命令执行没成功。
|
||
|
||

|
||
|
||
那就继续往下找找执行点在哪吧。
|
||
|
||

|
||
|
||
但是没找到,因为再往下走就开始准备响应了,所以最有可能的就是`web_getData`这个调用了,因为只有这个的传参是和请求细节高度相关的。
|
||
|
||
看看这个调用,发现是个动态库的接口。
|
||
|
||

|
||
|
||
全局搜一下,发现这个调用来源是`libmosquitto.so`。
|
||
|
||

|
||
|
||
但这不是`MQTT`嘛,难道这路由器的内部数据流动是靠的`MQTT`推送?
|
||
|
||
搜一下调用看看。
|
||
|
||

|
||
|
||
发现刚才那个`upgrade.so`库也调用过这个`MQTT`库。
|
||
|
||
那会不会这个`upgrade.so`库就是负责处理这个固件升级消息的呢?
|
||
|
||
结果发现这里面真有一个`setUpgradeFW`函数。
|
||
|
||

|
||
|
||
然后最关键的数据`FileName`是给了`v8`,然后`v8`出现的地方就这么三处。
|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||
下面两处本质上是一样的,只不过前置条件不同而已,而且这里确实是直接把`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``,这很明显是可以执行的嘛。
|
||
|
||
那么问题出在哪呢?难道是文件名被编码了?
|
||
|
||
尝试修改命令内容,直接输出文件名看看到底啥情况。
|
||
|
||

|
||
|
||
但是这个动态库是谁调的咱还不知道,去全局搜一下。
|
||
|
||

|
||
|
||
完全没有啊,难道是以文件夹为单位的动态加载?
|
||
|
||

|
||
|
||
发现有个`cste_sub`可执行文件,看看有没有这个进程,毕竟如果数据走`MQTT`的话,肯定是有后台进程的嘛。
|
||
|
||

|
||
|
||
还真有一个,那就替换一下刚才改的动态库,然后kill掉这个进程自己前台跑一下,看看有没有输出。
|
||
|
||

|
||
|
||
结果发现文件名是`/var/uImage.img`?
|
||
|
||
看看内容是啥。
|
||
|
||

|
||
|
||
这不是整个请求体嘛...为啥没解析就全存了,难道这个功能本来就有问题?
|
||
|
||
全局搜一下看看`uImage`这文件名是哪来的。
|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||
这...上传的文件名是写死的固定值,那咋实现可控的?
|
||
|
||
回头看看`cgi`,发现了个小细节。
|
||
|
||

|
||
|
||

|
||
|
||
如果`get`传参为空的话,最后会落到`124`行的`v3 = cJSON_Parse(v29);`,那么基于`get`传参为空这个前提,`v29`是否可控呢?
|
||
|
||

|
||
|
||
似乎确实可以,这个`stdin`应该就是请求体,因为上面`CONTENT_LENGTH`正好作为`read`的长度用了。
|
||
|
||
那么就想想怎么构造请求呗,既然`v29`完全是可以自己传的,那咱们只要把上面`{"topicurl":"setting/setUpgradeFW","FileName":"%s","Flags":"%d","ContentLength":"%s"}`填充一下然后通过`post`传进去好像就行?
|
||
|
||

|
||
|
||
看看输出的文件名,确实过来了。
|
||
|
||

|
||
|
||
那咱们把刚才那个动态库给替换回原来的(不然命令注入点没了),然后重新跑一下看看。
|
||
|
||

|
||
|
||
成功执行了`pwd`,再看看别的命令,比如说`ls -al /`。
|
||
|
||

|
||
|
||
漏洞成功复现。
|