前言 书接上回 ,由于我们学校实验室里正好用的是小米AX9000
这款路由器,因此当时我就选了这款设备挖,也就没有对固件仿真了。既有女朋友又努力上进的ZIKH26
师傅与我这个摆烂人不同,在复现我这个洞的时候,想要研究一下小米路由器的仿真模拟。正好最近我也没啥事,于是也就一起看了看。
恰巧前几天也有师傅私信我关于小米路由器该如何仿真的问题,并且网上目前似乎还没有师傅分析过小米固件的仿真,因此我就写了此文放了出来(所以,我说FirmAE
是对某些固件特制的学术玩具会不会被打,逃)。
除了一些特殊的设备,其实对于固件的仿真模拟,总结成一句话就是:IoT设备的仿真过程就是不断地改报错,只要与外设硬件无关,都是小问题 。也就是说,主要是要有耐心。
这里依然以小米路由器AX9000
为例,小米其他型号的路由器也都可以用如下的方案仿真。在本文的最后,会用我们仿真模拟的路由器环境验证CVE-2023-26315
这个漏洞。
环境配置 小米路由器AX9000
相对其他一些型号来说,仿真稍微复杂一些,因为小米AX9000
的固件是AArch64el
架构的,而网上似乎还没有公开的AArch64
的内核与文件系统。因此,我们需要自己装一个AArch64
的虚拟机,并从中extract
提取出内核与磁盘镜像。可参考这篇文章 中的步骤完成。
我本人比较懒,装AArch64
虚拟机很耗时间和内存,所以这部分是ZIKH26
师傅提取的内核与磁盘镜像,在此表示崇高的敬意。
【给看雪的家人们送个福利】 提取好的AArch64el
架构的vmlinuz
与initrd.img
可点击这里 下载(账户root/zikh,密码都是root,ssh需要使用普通用户再su),以及若有师傅想自己提取的话,详细步骤可见ZIKH26
师傅的博客 。这里与我下文有些不同的是,他文中用的是NAT
完成QEMU
与宿主机通信,我习惯于用桥接的方式。
关于网络环境配置的问题,这里就不再多说了。只简单提一句,qemu-system-aarch64
启动后,可以用ip addr
看到enp0s1
网卡是DOWN
的状态,且没有分配IP
地址。
因此,我们需要手动分配一个IP
地址(与网桥br0
处于同一网段即可,qemu
的enp0s1
接口与宿主机的eth0
接口通过虚拟网桥br0
转发数据),然后再将enp0s1
网卡给UP
启用即可,命令如下:
1
2
ip add add
192.168
.
192.132
/
24
dev enp0s1
ip link
set
enp0s1 up
完成后,enp0s1
网卡的状态应该正常了,再检测下qemu
虚拟机与宿主机能否互相正常通信:
下面,就正式开始仿真模拟了。
起手式 将固件解压后,首先将文件系统用scp
传入qemu
虚拟机,然后就是最经典的三行起手式:
1
2
3
mount
-
-
bind
/
proc proc
mount
-
-
bind
/
dev dev
chroot .
/
bin
/
sh
好吧,这一步我竟然还截了一张图:
根据openwrt
的内核初始化流程,按理说应该先启动/etc/preinit
,其中会执行/sbin/init
进行初始化,但是在这套固件仿真的时候,这样会导致qemu
重启,所以我们首先先执行/sbin/init
中最重要的/sbin/procd &
,启动进程管理器即可。
启动httpd服务 下面就该启动httpd
服务了,简单检索一下,发现有uhttpd
,mihttpd
,sysapihttpd
。进一步查看一下配置文件(如/etc/sysapihttpd/sysapihttpd.conf
),发现sysapihttpd
其实就是nginx
,监听了80
端口,有了nginx
自然就不需要再启动uhttpd
了。而mihttpd
中监听了8198
端口,定义了一些文件上传下载的API
,可以暂时先不启。
所以,只需要启动sysapihttpd
,执行/etc/init.d/sysapihttpd start
即可:
首先,报错缺失/var/lock/procd_sysapihttpd.lock
这个文件,这个创建一下对应的目录和文件就行了。接着,会报错Failed to connect to ubus
,很显然这里是用到了ubus
总线通信,我们需要启动/sbin/ubusd &
。但是,接下来又继续报错usock: No such file or directory
,但是并没有给出缺失哪个文件,因此我们需要将ubusd
拖进IDA
定位一下报错点。
很容易定位到sub_20B0
函数,我们执行的是ubusd
,而不是它的软链接tbusd
,因此v8
会是路径/var/run/ubus.sock
,接着其作为参数传入usock
函数中,当usock
函数的返回值错误时,就会走到perror("usock")
报错。
显然我们缺少/var/run/ubus.sock
这个文件,创建后ubusd
即可正常启动,接着sysapihttpd
服务也启动成功(这里缺失/dev/nvram
芯片可以先不用管,因为后续没有用到相关操作,暂时不用hook
):
到这里,检查一下进程里相关程序都已经挂起,且netstat
查看web
端口也正常对外开放:
崩溃&排查 虽然这一切看上去没有任何问题,nmap
也能扫到端口,但是我们访问IP
就会报错,显示服务器没有传回任何数据。
此外,查看进程,可以发现sysapihttpd
的主进程进程号没变,但是子进程的进程号却已经改变,这就说明当我们访问IP
的时候,server
崩溃了,所以子进程crash
了,主进程作为守护进程又开了个新的子进程。
因此,我们可以用strace
跟踪一下子进程的进程号,判断出现了什么问题导致sysapihttpd
会crash
的。这里直接在外面的qemu
文件系统中用apt
或者dpkg
安装一下strace_arm64
的deb
包,然后再ssh
开个窗口跟踪进程即可。先attach
上去卡住,然后访问IP
后,strace
跟踪的结果如下:
可以看到最后是发生了段错误导致了crash
的。我们需要解决的是上面的三个报错,前两个错误都是由于缺少/etc/TZ
文件所导致的,这是一个和时区有关的配置文件,echo "WAUST-8WAUDT" > /etc/TZ
创建一下即可。
对于后面getsockopt
的报错,我们可以将/usr/sbin/sysapihttpd
拖进IDA
,定位一下getsockopt
参数一致(主要关注第三个参数是0x50
)的位置,可以找到sub_1EDAC
和sub_27570
两个函数。至于是哪个函数中getsockopt
调用的位置才是关键,结合strace
上下文,根据上面调用了accept4
系统调用,可以确定sub_27570
函数中getsockopt
的位置才是调用点。
根据上面的代码,结合strace
后面输出的log
报错信息,可以得知这里是由于getsockopt
发生了错误,从而重定向到了0.0.0.1:65535
,导致了后续的崩溃。因此,最简单粗暴的办法就是绕过这个if
分支,不执行其中的内容,即将CBZ
给patch
成相反的CBNZ
即可:
将patch
后的sysapihttpd
程序按照如下的步骤更新至固件的文件系统中,并重启httpd
服务:
然后,访问IP
并重定向至/init.html
,可以看到路由器初始化配置页面终于是成功出现了:
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2024-6-6 14:00
被winmt编辑
,原因: 修改格式