一、环境配置
版本:AX3000_Pro_V16.03.49.26
EMUX:emux
二、EMUX配置
首先按照官方步骤配置EMUX环境:
docker run hello-world
sudo groupadd docker
sudo gpasswd -a $USER docker
sudo usermod -aG docker $USER
git clone --depth 1 --single-branch https://github.com/therealsaumil/emux.git
cd emux
./build-emux-volume
./build-emux-docker
如果build-emux-docker编译失败,设置DOCKER_BUILDKIT=0:
- DOCKER_BUILDKIT=1 docker build -t $OWNERNAME/$IMAGENAME:$TAGNAME \
-f Dockerfile-emux .
+ DOCKER_BUILDKIT=0 docker build -t $OWNERNAME/$IMAGENAME:$TAGNAME \
-f Dockerfile-emux .
之后执行./run-emux-docker即可进入EMUX环境。
三、TD路由配置
固件映像需要放置在EMUX项目的特定目录结构中。在 files/emux/firmware 目录下新建 TENDA-USAX3000 目录,结构如下:
.
├── config
├── kernel
│ └── gzImage-5.10.25-arm64
└── squashfs-root
config 为配置文件,kernel/ 下为Linux内核文件,squashfs-root/ 为路由器固件文件系统。
config 文件内容如下:
# Sample device configuration file
#
# The device ID should have a prefix of 'firmware/' or 'firmware-extra/'
# depending upon which parent directory you wish to place the firmware into
#
id=firmware/TENDA-USAX3000
#nvram=nvram.ini
rootfs=squashfs-root
randomize_va_space=0
#ld_preload=preload
initcommands="/etc_ro/init_tab"
然后在 firmware 下的 devices 文件中增加一条设备信息:
firmware/TENDA-USAX3000,qemu-system-aarch64,virt,cortex-a72,,256M,gzImage-5.10.25-arm64,VIRTARM64,TENDA-USAX3000 (AArch64 Simulation)
运行 ./build-emux-volume 构建卷,然后运行 ./run-emux-docker 进入EMUX Shell。
在EMUX Shell中运行 launcher 选择对应的路由器设备启动,或者新开终端运行 ./emux-docker-shell 后执行 launcher。之后会进入设备控制台,选择第三个选项直接进入路由器的固件Shell。
进入Shell后,路由器环境并未完全初始化。查看 inittab 可以发现,系统初始化脚本为 /etc_ro/init.d/rcS。可以手动执行此脚本或选择性地运行关键命令来启动基础服务。
尝试运行HTTPD服务:
/bin/httpd --debugger --verbose
会遇到错误。
四、HTTPD调试
HTTPD报错主要分为两部分:connect cfmd is error 和 goahead: setLocalHost error。优先解决GoAhead错误。
分析发现,GoAhead在初始化时需要获取 br0 网桥的IP地址。在模拟环境中手动创建并配置 br0 网桥:
ip link add name br0 type bridge
ip link set br0 up
ip addr add 192.168.100.2/24 dev br0
再次运行 httpd,GoAhead错误消失,但 connect cfmd is error 依然存在。不过HTTPD服务已成功启动。
根据EMUX的端口映射(例如 20080:80),访问宿主机的 20080 端口可以打开路由器登录页面,但无法通过任意密码登录。
五、CFM_SOCKET分析
登录验证涉及读取 default.cfg 配置文件,该操作通过连接Unix套接字 /var/cfm_socket 与 cfmd 守护进程通信完成。在模拟环境中,直接运行 /bin/cfmd 会因无法读取MTD设备而失败。
分析 libcommon.so 中的 GetValue 和 SetValue 函数,可以了解其与 cfmd 通信的消息格式。因此,我们无需完全模拟 cfmd,只需实现其通信协议即可。
六、CFM_SOCKET模拟
通信协议基本格式为 [4字节长度 + JSON字符串]。JSON结构为 {"type":数字, "name":"配置名", "value":"配置值"}。
为模拟该服务,我们需要:
- 将路由器的
default.cfg 转换为INI格式,便于读写。
- 编写一个Socket服务器,接收上述格式的消息,根据
type 和 name 查询或更新INI文件中的值,并返回响应。
由于路由器环境可能不支持Python,我们可以在宿主机上运行TCP服务,然后使用 socat 将路由器的Unix Socket请求转发到宿主机的TCP端口。
在EMUX Host Shell中执行:
socat UNIX-LISTEN:/emux/firmware/TENDA-USAX3000/squashfs-root/var/cfm_socket,fork,reuseaddr,unlink-early TCP:<宿主机IP>:8888
完成映射后,在路由器环境中再次运行 /bin/httpd,所有错误消失,服务正常启动。访问Web界面并设置密码后,可以成功登录管理页面。
七、漏洞分析
在 libgo.so 的 UploadCfg 函数中存在命令注入漏洞。该函数在处理配置文件上传时,直接将上传的文件名拼接进shell命令中执行,且未做任何过滤。
关键代码如下(伪代码逻辑):
snprintf(cmd, sizeof(cmd), “/bin/cp /tmp/%s /tmp/”, filename);
system(cmd);
漏洞利用
通过构造恶意的上传文件名,可以注入任意命令。例如,上传名为 ‘&busybox telnetd -l sh -p 9999&killall -9 httpd&’1.cfg 的文件。
& 用于在后台执行前面的cp命令并连接新命令。
busybox telnetd -l sh -p 9999 在端口9999启动telnet服务。
killall -9 httpd 终止HTTPD服务(可选,用于释放80端口或触发重启)。
- 最后的
& 确保整个注入序列在后台运行。
1.cfg 是实际被复制的文件名部分。
攻击成功后,可以通过telnet连接路由器的9999端口,获得一个shell。
POC脚本概述
利用脚本主要步骤如下:
- 登录:通过Web接口登录,获取认证Token(
stok, sign)。
- 构造并发送恶意请求:向
/cgi-bin/UploadCfg 接口发送一个multipart/form-data请求,其中文件名字段包含注入的命令。
- 验证:等待片刻后,尝试连接注入命令中指定的端口(如9999),确认telnet服务是否成功启动。
此漏洞的利用条件是需要具备登录后台的权限。整个研究过程展示了使用EMUX等工具进行IoT固件模拟、分析和漏洞复现的完整流程,是物联网安全研究中的一项典型实践。