入门MQTT漏挖:从概念,底层原理到rce

前言

别的事情搞得差不多了,又回来继续学习啦
这篇存在大量借鉴、复现别人的东西,属于是初学的一个记录吧,在这个过程中引入了自己的理解

MQTT协议简介

首先,摘自wiki百科简体中文页面:

消息队列遥测传输(英语:Message Queuing Telemetry Transport,MQTT)是根据ISO 标准(ISO/IEC PRF 20922)下基于发布(Publish)/订阅(Subscribe)范式的消息协议,可视为“资料传递的桥梁”。它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议。为此,它需要一个消息中间件(如HTTP),以解决当前繁重的资料传输协议。

总的来说,MQTT的特点就是:
基于发布-订阅机制的、适合网络情况不好的情况下进行可靠通信的协议:发布-订阅可以不是即时通信那种,可以先把信息放在中间服务器,等另一方重新连接到网络以后再领取信息
而且协议简单,是适合性能较低的设备的

凭借简单易实现、支持 QoS、报文小等特点,占据了物联网协议的半壁江山

image.png
事实上,这个设备不同于zigbee之类的物联网协议,其不需要特定的网卡,只需要网络连接,其仍然基于TCP/IP,而且需要wifi或以太网的连接才能使用这个协议,换言之,其没有创建自己的一套通信的东西,用的还是原来有限无线网络的东西
官方介绍:https://mosquitto.org/man/mqtt-7.html

MQTT网络

image.png
MQTT通信协议里,主要分为三个角色:【发送方】-【中间方】-【接收方】
【发送方】Publisher: 消息的发布方
【中间方】MQTT Broker(有的地方称之为MQTT server):消息中间处理的一方,负责对每个主题(Topic)的最后一条消息(Retained Message)进行保留
【接收方】Subscriber:消息的订阅方,也就是在MQTT中间服务器里面取用消息的用户

发布/订阅模式 相对于 客户端/服务器模式 的好处在于:
①发布者和消费者之间不必预先知道对方的存在,比如不需要预先沟通对方的 IP Address 和 Port
②发布者和消费者之间不必同时运行。因为 Broker 是一直运行的。

MQTT报文

根据https://bbs.kanxue.com/thread-255312.htm
image.png

  • 固定头(Fixed header),存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识;
  • 可变头(Variable header),存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容;
  • 消息体(Payload),存在于部分MQTT数据包中,表示客户端收到的具体内容;
    image.png
    可以看到,报文的消息类型(message type)本身可以分为这几类:
  • 对网络拓扑的变更(建立、取消订阅与确认取消订阅,建立、取消客户端与服务端连接与确认建立连接)
  • 对网络状态的确认报文(ping的请求确认与回复)
  • 对消息的发送接收状态的报文(发布消息与消息的收到、释放(删除)、完成)
  • 消息发送内容本身,这里往往是存在漏洞的地方,消息处理不当即可能存在漏洞

MQTT Topic

对于消息的发送、保留、订阅,其中最重要的概念之一就是Topic(主题)
不同的主题互相独立地进行发送、保留、订阅
而发送方可以发送信息到任意主题,接收方也可以接收任意主题的信息——只需在报文里指定
MQTT 的主题是不需要预先创建的,发布者发送消息到某个主题、或者订阅者订阅某个主题的时候,Broker 就会自动创建这个主题。
至于指定时的格式,可以看看,不同的主题级别用斜杠/作为分隔符号
(也可以用到再看吧,反正贴在这里先:P)
取用这位师傅的文字表述https://bbs.kanxue.com/thread-260426.htm
MQTT 的 Topic 有层级结构,并且支持通配符 + 和 #:

  • + 是匹配单层的通配符。比如 news/+ 可以匹配 news/sportsnews/+/basketball 可匹配到 news/sports/basketball
  • # 是一到多层的通配符。比如 news/# 可以匹配 news、 news/sportsnews/sports/basketball 以及 news/sports/basketball/x 等等。
    wiki上的说实话写的有点复杂,但是更细
    image.png

三个可选的 QoS 等级

再度取用这位emqttx师傅的文字:

为适应设备不同的网络环境,MQTT 设计了 3 个 QoS 等级,0, 1, 2:

  • At most once (0)
  • At least once (1)
  • Exactly once (2)

QoS 0 是一种 “fire and forget” (发了就不管)的消息发送模式:Sender (可能是 Publisher 或者 Broker) 发送一条消息之后,就不再关心它有没有发送到对方,也不设置任何重发机制。

QoS 1 包含了简单的重发机制,Sender 发送消息之后等待接收者的 ACK,如果没收到 ACK 则重新发送消息。这种模式能保证消息至少能到达一次,但无法保证消息重复。(有确认报文就行但不管多少次)

QoS 2 设计了略微复杂的重发和重复消息发现机制,保证消息到达对方并且严格值到达一次。

MQTT安全问题

综上所述,可以注意到三个不够安全的地方:
1.原生版本没有鉴权机制,谁都能加入MQTT网络:原生的MQTT协议本身过于简单,没有实现任何鉴权机制,这就需要在其它层次实现更安全的网络加入机制,否则相当于谁都可以加入MQTT网络收发信息
2.原生版本消息未加密:消息发送内容本身的数据包是没有加密的,不过实际上厂商会自己二开一些加密措施,使用MQTT衍生版本(比如MQTT over WebSocket),或者启用Broker的安全措施,这样如果不知道密钥,加入网络也无法正常通信和偷听消息
以上问题都在具体实现时得到了很好的缓解,but
3.消息处理问题:消息发送内容本身没有过滤,发送什么都可以,这里如果处理不当可能产生某些漏洞,这是本文后面重点关注的地方

其余通信相关的特性,请移步MQTT wiki百科,对于漏挖来说以上知识铺垫好像就足够了

MQTT协议主流中间服务器

这篇文章从总体上讲述了主流MQTT broker(server)的种类,进行全面详尽的比较,从GitHub Star 数、贡献者和 issue 的数量、用户下载量和 Docker 拉取数等等的方面去分析了每种MQTT目前的受欢迎程度,以及其它方面,做了极其详尽的比较:
https://www.modb.pro/db/1744920590174724096
image.png
MQTT真的可以说是经久不衰的一款协议了,居然一直有人在写新的实现并且维护,其轻量级的设计还是非常适合现在的低功耗设备的,甚至很多电脑、手机的程序都在用,所以真的有必要好好学一下O(∩_∩)O

重要的是,这些broker积极引入其它的安全机制来补充MQTT自身短板,都支持了基于 TLS/SSL 的安全连接以及身份验证和授权机制,例如用户名/密码认证、JWT、X.509 证书和访问控制列表。
image.png

询问了一下师傅们,现在在IOT上用的最最广泛的还是Mosquitto,其基于C/C++,而且诞生最早
根据https://www.modb.pro/db/1744920590174724096:

Mosquitto 项目由 Roger Light 于 2009 年创立,后来捐赠给 Eclipse 基金会,遵循 Eclipse公共许可证(EPL/EDL 许可证)协议。截至 2023 年 12 月,它是部署最广泛的开源 MQTT Broker,拥有庞大的社区,在 GitHub 获得了超过 8k 个 Star。
Mosquitto 由 C/C++ 编写,采用单线程结构。它支持 MQTT 协议的 5.0、3.1.1 和 3.1 版本,并支持 SSL/TLS 和 WebSocket。由于其轻量级设计,Mosquitto 非常适合部署在嵌入式设备或资源受限的服务器上。
Mosquitto 因其仅占用约 200KB 的启动内存而广受赞誉。但是,它并不支持多线程或集群功能。Mosquitto 可在多个平台上运行,包括 Linux、Windows 和 macOS
官网:https://mosquitto.org/
GitHub:https://github.com/eclipse/mosquitto

噢,好像都是有github的👁真好啊,那可太方便对比着进行”逆向”分析了^o^

入门MQTT漏洞分析之:mosquitto

突然想系统学习mqtt是看到了这位Nameless_a师傅的文章:https://bbs.kanxue.com/thread-281231.htm,感谢师傅的分析和保存的附件能够让我学习到了这方面的漏挖哈哈,固件下载请移步至师傅的文章

固件提取

(这部分因为咱手上没有题目给的硬件,不能复现QwQ,但就假设一下下,我们已经获得了固件,好不好b( ̄▽ ̄)d )

固件文件系统分析

从我自己的理解去复现并总结一下,这道题是怎么从固件到最后在mqtt协议上找到rce点并构造exp的,以供之后参考自用
首先进行固件解包,很顺利的接触到了完整文件系统
image.png
紧接着,既然已知题目是让我们在mqtt里面找rce,那就直接grep -r mqtt看看先~
image.png

我们得到了以下三个文件:

  • etc/mosquitto.conf (mosquitto配置文件)
  • sbin/mosquitto (mqtt的主程序)
  • sbin/mosquitto_sub(Topic处理相关逻辑往往在这里)
    噢原来是mosquitto的,然后,我们grep -r mosquitto一下
    image.png
    又发现了新东西, lib/libmosquitto.so.1,这其实就是mosquitto的库函数所在了,有了这个库函数其实也能进一步分析其逻辑
    (不过讲真直接看github的这个libc库代码就很清晰,哈哈)
    最关键的是,这个

    我们可以从中知道mosquitto有这些设定:
    账号:admin,密码:123456
    /etc/mosquitto.conf是使用的配置文件
    使用参数-t指定了两个主题(Topic):block,logs,参考的是官方的help输出
    image.png
    image.png

漏洞分析正是从对主题的交互处理进行

漏洞分析

Mosquito的密钥

/etc/mosquitto.conf
image.png
可以看到不允许匿名登录,而且有密钥
如果没有办法接触到这个文件,那就mqttpwn,但是现在有,那就试下cat一下
image.png
诶不是说好user是root吗,怎么这里又admin了,奇怪,不过以这里为准的吧
看起来很像passwd和shadow里面那个东西,实际上,这个是SHA-512加盐(salted)哈希算法,盐是”72+eZeM9BBsMhW22″,密钥是”OIxuvlytYV89o7vtTl2EK+CnzaBdDevDcA5u1nvR7Dj99j0yXGayD+6eHQBmj9/CMFzfyQZ4htrpPv4qNinvww==\”的base64解码版本
实际上这个是强密码来的。。。解不开,那就看看https://bbs.kanxue.com/thread-281231.htm作者的原话:
image.png
原来dump出板子的密码才是真的,也跟着跑一下看看:
image.png
xhlj2024:2758934是mqtt交互的账户和密钥

核心mqtt信息处理函数定位

实际上,mosquitto_sub是管理主题订阅和发布的程序,ida分析,搜索关键字block
image.png
(实际上logs如果在strings窗口搜索是搜不到的因为这字符串太短了,但是其事实上存在的):
image.png

追至 block,log都在的sub_410300函数,可以见到两个strcmp逻辑
大胆猜测,这些字符串唯一所在的地方,就是用户使用mqtt时,报文里面指定的主题了,那么这里很有可能就是相关逻辑
image.png
事实上如果我们在这个位置退一步思考,就会发现这个函数属于这样一条调用链:
mosquitto_message_v5_callback_set->sub_401F50->sub_410300
(由于符号表不被完整保留,有名字的都是库函数,而没名字的都是用户自定义消息处理部分,这样的话思路就比较清晰了,官方库函数不容易有漏洞,ctf不可能叫我们去挖官方库0day,而用户实现部分往往极有可能存在漏洞)
而这个 mosquitto_message_v5_callback_set大有来头,我们可以找到它的源代码:
https://github.com/zc110747/remote_manage/blob/6a9531fe859f7113bdc7b75b19618abdf9c1939a/thirdparts/mosquitto/include/mosquitto.h#L2138
image.png
我们可以看到介绍说
Set the subscribe callback. This is called when the library receives a SUBACK message in response to a SUBSCRIBE.
意思是,设置订阅相关回调函数,这会在库接收到SUBACK(确认订阅报文)时被调用,来作为对订阅的回应
image.png
这个函数:

mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props));

我们可以看到它有两个参数,其中第二个是一个函数指针(对应sub_401F50),里面的第三个参数(a3)就是传递的message
而sub_401F50的a3:
image.png
被传入了sub_410300,作为第二参数
综上,接下来我们分析sub_410300以及其第二参数做了什么,就好了

核心处理函数sub_410300

刚开始,a2传入以后就取了+4部分,什么情况,心想这里肯定有个结构体
image.png
果不其然,又是在这里找到了https://github.com/flightonary/Moscapsule/blob/76f5d85f81c2841f8a110a7a3e7e5089beb47aed/mosquitto/lib/mosquitto.h#L1164
image.png
+4显然就是topic所在
所以看得出来,这里有对topic进行比对,正是先前定义的block和logs,对上了,所以strcmp如果为假,那么,就去到对应topic的处理逻辑
image.png
可以看见,block的主题部分我们是无法控制的,其没有可控rce的点,而其实logs的主题会直接go to LABEL_9,也没东西
image.png
似乎这两个topic都。。。挺没意思的? o(╯□╰)o

重点来了!然而,在topic不为logs以及block的时候,其后面却仍然会对mosquitto_message结构体的payload部分(a2+8),
image.png

进行某种处理,并且放入result,后面简单分析了一下,这个处理实际上就是放进某种数据结构里,以供后面翻找——在这一点上个人觉得不需要逆的太用力,只要知道这是一对操作即可,把payload文字存到数据结构,然后取出,两个细节都有点复杂但一定是成对的
image.png
然后在save逻辑里面找到sub_4042B4,其实从内部针对一些字符({,},”,:,-等)大概可以推敲出其正在处理json字符串,
image.png

image.png

但sub_4042B4实在复杂的有点让人害怕,没有完整摸清逻辑,sry (:з」∠)
总之知道这个存入结构的sub_410300,确实是依据于JSON,这样就够啦,结构体的payload部分是JSON,就酱

sub_410300之:mosquitto_message结构体的payload部分,哪部分真正可能rce

紧接着可以重命名一些变量,那些被从payload里解析的变量
image.png
然后我们发现,距离走到有system还有三个拦路虎,约束了输入
image.png
首先,无论是log,timestamp,info,都得在mosquitto_message结构体的payload里有所提交,否则不会进入后面的逻辑直接return了
其次,the_log索引[5]位置的值不能等于1
再者,timestamp处有输入校验函数sub_41006C,一定要满足时间戳格式,否则也不会进入后面的逻辑(至于是什么模式,直接丢gpt了)
image.png

这意味着timestamp不会纳入注入点的考虑范围
紧接着,终于到了漏洞可能存在的逻辑:
image.png

其中,第一个把timestamp的+4位置,拼接到了命令进行执行,而从前面可以知道,我们没有办法控制它变成理想的命令执行字符串,因为有严格的时间戳格式校验
第二个则进行了神秘的处理?(哎呀忘记把分析时对变量的重命名取消掉了,实际上没有给)
对这一大段处理的理解,将直接决定rce能否成功,而很显然,info部分,已经是其中最有可能rce的点

sub_407654:SM4密钥扩展

辛苦看到这里,不过,真正有意思的东西来了(:з」∠)
打开findcrypt插件,可以发现两种编码或加密算法的特征
image.png
看来是有base64,和sm4加解密
追踪base64的引用发现是这样的,看起来ida认为这个表被加载到load段不知道啥地方了,不过没关系,知道其存在就行
image.png
load段相关代码,暂时看不出来的样子
image.png
然而我们追踪sm4的固定特征,被在哪调用时,bingo,就是刚刚那里
image.png
可以看到,sub_407654这里,这些固定特征(实际上就是固定的变换表)被用于固定的异或变换了
image.png
image.png
实际上,了解SM4以后就知道,所谓加密算法检测特征就是这些个用于密钥扩充的常数
image.png

image.png

image.png
然后可以发现,sub_407654的后面是不断在重复取用某些固定数字来进行相同的异或操作,直到最后
image.png
而且取用的文字就是前述固定参数CK
image.png

最后把内容都放给了a2并分开32次存放,与固定参数CK长度相同
image.png

实际上这就是在进行密钥扩充,且a1是扩充前密钥,a2是扩充后密钥,综上,sub_407654即密钥扩充函数
key在这儿
image.png

sub_40C1D0:SM4解密函数

而扩充后的密钥,有被使用且仅仅只被用于sub_40C1D0,那等于明说sub_40C1D0就是解密函数了,接下来就简单了
image.png
既然知道sub_40C1D0是解密函数,那不需要太多分析了,直接看输入什么,输出什么,就能猜到哪个参数存放密文,哪个参数存放明文
显然a1是密文,在整个加密流程里被首先放入
image.png

在最后的最后,a2被放入每轮处理的结果,显然它就是存放解密后文字的地方,
image.png

sub_403B50:base64解码

好,SM4我们知道了,而且看起来,Ouput1经过一个神秘函数,被放入到了cmd,那前面呢,对于rce的可能密文,来自于v13区域,而且我们知道v12,v13,v14指向相同内容,而v12,被开辟了v11(the info)+4处,指定大小的区域,然后v10(其正是指向(the info)+4内容)、v11、v12,被传入了神秘的sub_403B50函数,进行了不为人知的变换
(这段,好拗口,好难理解,其实实际分析的时候知道sub_403B50肯定给了v12东西,而且v11是v10内容的指定长度就行,这是很明显的传入了“输入指针、输入长度、输出指针”三个要素,那肯定sub_403B50是某种处理函数)
image.png
跟进sub_403B50,注意到刚开始有不等于等号的判断(base64的填充字符,使用if跳过处理),且有可见字符范围判断
image.png
dword_415858是什么?实际上点击dword_415858
image.png
划定415858部分,按a键
image.png
实际上415858+可见字符的ascii码*4,会到对应的位置,总之,dword_415858 + xxx的形式,会对照到具体的ascii字符,无论可见还是不可见,而大小写字母、数字,其偏移会去到BASE64_table_4158D8内部
总之这就是个Base64解码函数

sub_410254:过滤函数

知晓base64解码,sm4解密后,system已经在向我们招手,等等,sub_410254又是啥东西(:з」∠)

image.png
追踪sub_410254这个,最后的拦路虎(•́へ•́)
image.png
(千万千万要打开反斜杠\的类型转换显示,忘记打开后,这个点让我疑惑了一个小时前前后后反复看QwQ)
可以看到,过滤了反引号`,噢,这是命令注入的过滤,看来这块负责过滤命令注入
约束1:
(unsigned int)(unsigned __int8)checking_word – 123 \< 2 这块刚开始没开强制转换的显示,导致我直接以为ascii小于125就不行,那就等于所有可见字符都不行 实际上,ascii会被转换成无符号整数,那么,正在检查的字符,实际上不能是123,123+1,对应的是”{“,”}” 约束2: v6 = (unsigned __int8)(checking_word – 36); if v6 < 24, ((0x800015u >> v6) & 1) 则一定要不成立
image.png

综上,过滤了:反引号(`),and符号”&”, 刀乐符号”$”, “(“,分号”;”,大括号”{“, “}”被过滤掉了
然而,换行符,|符号,都没有被过滤,可以绕过

综上所述

综上,漏洞点在:
xhlj2024:2758934是mqtt交互的账户和密钥
不能提交topic=logs,block,要提交别的文字,什么都可以
漏洞点需要我们提交的请求里面,mqtt的交流载荷(payload)要精心构造一个JSON,其必须包含log,timestamp,info三个部分
timestamp必须是正确的时间戳格式,具体而言,必须类似于,11-45-14:19:19
log没有注入的作用,但其[5]索引处必须为1
info部分经过了base64,然后再sm4解密,密钥已知为:
key[0] = 0x9845DC01;
key[1] = 0x10CD5489;
key[2] = 0x67BA23FE;
key[3] = 0xEF32AB76;
最后,构造的解密后的命令注入语句,必须用|或换行符来分割命令,别的不太行

EXP构造

文章https://bbs.kanxue.com/thread-281231.htm已经给出exp,但我真的想搞懂一下:

首先,原来python就有mqtt的交互库:import paho.mqtt.client as mqtt
然后,最核心还是在get_input这块的交互上

image.png

v24 = [0x9845DC01, 0x10CD5489, 0x67BA23FE, 0xEF32AB76]
这就是之前找到那个密钥
但是我们需要使用正确的端序去打包image.png
然后就是对命令进行拼接,直接传给调用SM4加密的封装函数,
image.png
还是有点东西的,ecb(Electronic Codebook)模式的sm4
image.png

然后经过一轮base64编码后,就成了完整流程的info输入
image.png
最后拼接到这样预设的mqtt payload,然后调用mqtt的通信库image.png
可以从大量的看出作者调试了好久,果然分析容易,最后打通还是要调来调去的,可惜手上没有原题板子,作罢

总结

经过这两三天的学习,对mqtt的rce挖掘流程有了大致的理解

参考文章

https://zh.wikipedia.org/wiki/MQTT 从整体上介绍了MQTT协议
https://mosquitto.org/man/mqtt-7.html 官方介绍
https://bbs.kanxue.com/thread-260426.htm MQTT相关介绍,全面而又简洁,推荐
https://www.modb.pro/db/1744920590174724096 对比了2024主流的MQTT Broker
https://www.51cto.com/article/670429.html 把MQTT的消息传递机制讲的很透彻
https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/03-ControlPackets.html 更详细的MQTT报文解释
https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/05-Security.html 目前的MQTT最佳安全实践

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
粤ICP备20015830号