一
前言
AX9000
这款路由器,因此当时我就选了这款设备挖,也就没有对固件仿真了。AX9000
为例,小米其他型号的路由器也都可以用如下的方案仿真。在本文的最后,会用我们仿真模拟的路由器环境验证CVE-2023-26315
这个漏洞。二
环境配置
AX9000
相对其他一些型号来说,仿真稍微复杂一些,因为小米AX9000
的固件是AArch64el
架构的,而网上似乎还没有公开的AArch64
的内核与文件系统。因此,我们需要自己装一个AArch64
的虚拟机,并从中extract
提取出内核与磁盘镜像。可参考这篇文章(https://www.diozero.com/boards/qemuaarch64_bullseye.html)中的步骤完成。AArch64
虚拟机很耗时间和内存,所以这部分是ZIKH26
师傅提取的内核与磁盘镜像,在此表示崇高的敬意。AArch64el
架构的vmlinuz
与initrd.img
可点击这里下载(https://drive.google.com/file/d/1FcbCkfGuHlvohGlzA-HRRyM8izqhHALE/view?usp=sharing,账户root/zikh,密码都是root,ssh需要使用普通用户再su),以及若有师傅想自己提取的话,详细步骤可见ZIKH26
师傅的博客(https://zikh26.github.io/posts/3d9490d.html)。这里与我下文有些不同的是,他文中用的是NAT
完成QEMU
与宿主机通信,我习惯于用桥接的方式。qemu-system-aarch64
启动后,可以用ip addr
看到enp0s1
网卡是DOWN
的状态,且没有分配IP
地址。IP
地址(与网桥br0
处于同一网段即可,qemu
的enp0s1
接口与宿主机的eth0
接口通过虚拟网桥br0
转发数据),然后再将enp0s1
网卡给UP
启用即可,命令如下:ip add add 192.168.192.132/24 dev enp0s1
ip link set enp0s1 up
enp0s1
网卡的状态应该正常了,再检测下qemu
虚拟机与宿主机能否互相正常通信:三
起手式
scp
传入qemu
虚拟机,然后就是最经典的三行起手式: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
,可以看到路由器初始化配置页面终于是成功出现了:六
跳过初始化配置
grep
在固件文件系统中查找是在哪里重定向至/init.html
的,很容易定位到/usr/lib/lua/luci/view/web/sysauth.htm
文件:if
判断的条件,使之不重定向至/init.html
。根据这里调用的函数,很容易定位到如下代码的调用关系(这里的源码来自于小米路由器4 Pro 稳定版,https://cdn.cnbj1.fds.api.mi-img.com/xiaoqiang/rom/r1350/miwifi_r1350_firmware_7449d_1.0.31.bin,这个版本的Lua
源码没有编译,小米各个型号路由器的Lua
部分变化不大,源码当然比反编译出来的伪代码好看很多):uci
配置项,标记为已初始化即可:uci set xiaoqiang.common.INITTED=1
uci commit
uci set
设置后,再次访问IP
,即可绕过初始化配置,跳转至登录页面:七
设置登录密码
CVE-2023-26315
是一个授权认证后的漏洞,因此我们需要设置登录密码以登录进后台拿到token
的值(当然,上文说过有些型号的设备固件中Lua
是没有编译的源码,因此也可以拿这部分源码把相关API
改成未授权的,然后用固件里自带的/usr/bin/luac
编译一下替换即可,但这样确实太暴力了)。/usr/lib/lua/luci/dispatcher.lua
的jsonauth
函数中,其中调用了checkUser
函数根据从POST
报文中获取的username
,password
和nonce
(现时)字段进行身份验证。/usr/lib/lua/xiaoqiang/util/XQSecureUtil.lua
的checkUser
函数中,首先获取了系统uci
配置项中存储的密码,这里的XQPreference.get
函数在本文的上一节中已经给出,可分析出此处的配置项为account.common.(用户名)
。接着,需要POST
报文中传入的现时字段nonce
与系统中uci
存储的password
的值拼接后进行sha1
哈希的结果等于POST
报文中传入的密码字段。POST
报文中传入的密码和用户名字段是什么。很显然,POST
请求报文中的密码字段不可能是明文的形式,不然随便拦截一下就寄了。故而,一定会有相关的JavaScript
代码对用户提交的密码进行加密(哈希)后再进行传输。所以,可以直接在浏览器登录页面中,查看一下相关的web
代码:admin
,而密码字段是通过oldPwd()
函数加密后的结果。这里的oldPwd()
函数将用户提交的密码明文与一个固定的key
值(a2ffa5c9be07488bbb04a3a47d3c5f6a
)拼接后,进行sha1
哈希,再将结果继续与现时nonce
拼接后,再sha1
哈希一次,作为POST
请求报文中的密码字段。account.common.admin
这个uci
配置项设置为sha1(登录密码+key)
,比如说登录密码设置为winmt
,那么这个值就是sha1(winmta2ffa5c9be07488bbb04a3a47d3c5f6a)=b264db0fca361ef8eca919fa28e70d7a57d4c2db
。set
设置uci
的配置项:uci set account.common.admin=b264db0fca361ef8eca919fa28e70d7a57d4c2db
uci commit
account.common.admin
这个uci
配置项后,用设定的密码winmt
即可成功登录入路由器后台:token
值为789c292990be8ca7d36947145aaea700
。八
一个小插曲
sysauth.htm
中重定向的if
分支都注释掉的话,在后面进行漏洞验证的时候会出现一些问题。具体来说,是当访问/api/xqdatacenter/request
这个API
的时候,会出现503
报错。/usr/lib/lua/luci/controller/api/xqdatacenter.lua
中,对于/api/xqdatacenter/request
这个entry{}
没有设置第五个参数与权限有关的flag
位:entry({"api", "xqdatacenter", "request"}, call("tunnelRequest"), _(""), 301)
/usr/lib/lua/luci/dispatcher.lua
文件中,可以看到entry{}
的相关定义:--- Create a new dispatching node and define common parameters.
-- @param path Virtual path
-- @param target Target function to call when dispatched.
-- @param title Destination node title
-- @param order Destination node order value (optional)
-- @param flag For extension (optional)
-- @return Dispatching tree node
function entry(path, target, title, order, flag)
local c = node(unpack(path))c.target = target
c.title = title
c.order = order
c.flag = flag
c.module = getfenv(2)._NAMEreturn c
end
/usr/lib/lua/luci/dispatcher.lua
文件中,其内的dispatch
函数中对API
作了最顶层的权限校验(sysauth_authenticator
内的函数等各种模式是在其中被调用做进一步验证的)。在dispatch
函数中,有一段如下代码:if not _noinitAccessAllowed(track.flag) then
luci.http.status(403, "Forbidden")
return
end
_noinitAccessAllowed
函数的代码如下:function _noinitAccessAllowed(flag)
local xqsys = require("xiaoqiang.util.XQSysUtil")
if xqsys.getInitInfo() then
return true
else
if flag == nil then
return false
end
if bit.band(flag, 0x08) == 0x08 then
return true
else
return false
end
end
end
getInitInfo()
函数的返回值为true
或flag
位为0x08
(或& 0x08 == 0x08
),才能绕过这个_noinitAccessAllowed
的校验,不然就会报503
错误。getInitInfo()
函数我们已经不陌生了,在“跳过初始化配置”一节中出现过,也就是xiaoqiang.common.INITTED
这个uci
配置项。这样也就解释了为什么在处理sysauth.htm
中重定向的if
分支时,还是最好设置一下相关uci
配置项。当然,直接找一份xqdatacenter.lua
的源码,在对应entry{}
中加上flag
位0x08
,再用固件自带的luac
编译后替换一下也是可以的。九
验证漏洞
CVE-2023-26315
这个漏洞。根据我上篇文章(https://bbs.kanxue.com/thread-281901.htm)中的分析,想要完成此漏洞的验证,还需要启动datacenter
以及plugincenter
,命令如下:/usr/sbin/datacenter &
/usr/sbin/plugincenter &
netstat
查看9090
和9091
端口正常即可:exp
即可成功拿到shell
,漏洞验证完成:看雪ID:winmt
https://bbs.kanxue.com/user-home-949925.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多