freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

MQTT即时通讯协议安全分析
2022-03-21 10:22:40
所属地 广东省

1. MQTT协议

1.1 介绍

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务,是一种低开销、低带宽占用的即时通讯协议。MQTT协议运行于TCP之上,属于应用层协议。

1.2 消息格式

一个固定的报头,一个可变报文头、一个负荷

固定报文头|可变报文头|负荷

固定报文头(Fix Header)

至少两个字节,第一个字节包含消息类型和QoS级别等标志位。第二个字节是剩余长度字段,该长度是后面的可变报文头加消息负载的总长度,该字段做多允许四个字节。

单个字节最大长度是127字节,为什么不是256字节呢?MQTT协议规定,单个字节第八位若为1,则表示后续还有字节存在,第八位起"延续位"的作用。

MQTT协议最多允许使用四个字节表示剩余长度,并且最后一个字节最大值只能是0x7F不能是0xff,所以只能发送信息长度是256MB,而不是512MB。

可变报文头(Variable Header)

包含协议名、协议版本、连接标志、心跳间隔时间、连接返回码、主题名等

有效负荷(Payload)

理解为消息主体(body)

MQTT消息类型

MQTT发送的消息类型

CONNECT(连接)

PUBLISH(订阅)

SUBSCRIBE(订阅确认)

UNSUBSCRIBE(取消订阅)

固件报文头中的第一个字节包含连接标志(Connect Flags),该标志用来区分MQTT的消息类型

类型名称类型值流动方向说明
Reserved0禁止保留
CONNECT1客户端到服务端发起连接
CONNACK2服务端到客户端连接确认
PUBLISH3两个方向都可以发布消息
PUBACK4两个方向都可以Qos1消息确认
PUBREC5两个方向都可以Qos2消息回执
PUBREL6两个方向都可以Qos2消息释放
PUBCOMP7两个方向都可以Qos2消息完成
SUBSCRIBE8客户端到服务端订阅请求
SUBACK9服务端到客户端订阅确认
UNSUBSCRIBE10客户端到服务端取消订阅
UNSUBACK11服务端到客户端取消订阅确认
PINGREQ12客户端到服务端心跳请求
PINGRESP13客户端到服务端心跳响应
DISCONNECT14客户端到服务端断开连接
Reserved15禁止保留

消息质量

三个等级

QoS 0

最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息指挥到达一次。

QoS 1

至少分发一次。服务器的消息接收有PUBACk消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。

QoS 2

只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。

举例

共享单车锁,定时使用Qos Level 0质量消息请求服务器,发送单车的当前位置,如果服务器没有收到这个消息也没有关系,过段时间也会再发送一次。找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机APP不断向单车发送解锁消息,确保有一次消息能到达以解锁单车。用户用完单车之后,需要提交付款表单,可以使用QoS Level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱。

遗愿标志(Will Flag)

标志字段

Will Flag

Will Qos

Will Retain Flag

服务端和客户端通信时,当遇到异常或客户端心跳超时的情况,MQTT服务器会替客户端发布一个Will消息。如果服务器收到来自客户端的DISCONNECT消息,则不会触发Will消息的发送。

Will字段可以应用于设备掉线后需要通知用户的场景。

连接保活心跳机制(Keep Alive Timer)

MQTT客户端可以设置一个心跳间隔时间(Keep Alive Timers),表示再每个心跳间隔时间内发送一条消息。如果在这个时间周期内,没有业务数据相关的信息,客户端会发一个PINGREQ消息,相应的,服务器返回一个PINGRESP消息进行确认。如果服务器在一个半心跳间隔时间周期内没有收到来自客户端的消息,就会断开于客户端的连接。心跳间隔时间最大值大约可以设置位18个小时,0值意味着不断开。

异步发布/订阅实现

MQTT可以双向通信,MQTT支持服务端反向控制设备,设备可以订阅某个主题,然后发布者对该主题进行发布消息,设备收到消息后即可进行一些列操作。

MQTT安全

MQTT运行于TCP层以上并以明文传输,这就相当于HTTP的明文传输。

风险:

设备被盗用

客户端和服务端的静态数据被修改

拒绝服务攻击

通信被拦截、修改、重定向

虚拟控制报文注入

MQTT安全从三个方面考虑

应用层:MQTT提供客户标识以及用户名和密码,在应用层验证设备

传输层:类似于HTTPS,MQTT基于TCP连接,也可以加上一层TLS,防止中间人攻击

网络层:专线或者vp/n来连接设备与MQTT代理

认证

两种层次的认证:

1、应用层:MQTT支持客户标识、用户名和密码

2、传输层:传输层可以使用TLS,除了加密通讯,还可以使用x509证书来认证设备

客户标识

一般来说可以使用嵌入式芯片的MAC地址或者芯片序列号

用户名和密码

MQTT协议可以通过CONNECT消息发送username和password

由于以明文形式传输,所以使用抓包工具可以轻易的获取。

传输层认证

MQTT代理在TLS握手成功后可以继续发送客户端的x509证书来验证设备,如果不合法便可中断连接。使用x509认证的好处是,在传输层就可以验证设备的合法性,在发送Connect消息之前便可以阻断非法设备的连接,节省不必要的资源浪费,确保消息的完整性和保密性。

用户数据格式

十六进制或二进制

流量控制在非常小

字符串

JSON

JSON层次结构清晰,易于阅读和编写,易于机器解析和生成,有效提升网络传输效率

{“String":"Hello World!","Value":12345} JSON字符串

XML

MQTT协议只负责通信部分,用户可以自己选择

MQTT+JSON是目前最优方案。

1.3 MQTT通信

多个节点通过订阅同一个主题进行相互通信,通过tomqtt来指定发送目标及获取消息来源

tomqtt概念(类似目录结构)

某品牌汽车锁车协议主题remote_control/result/rvs_set_doorlock

一个或者多个主题

主题层级分隔符"/"

单层通配符"+"

多层通配符"#"

一般mqtt代理都会使用$开头的tomqtt作为系统调试、存储信息专用的tomqtt

1.4 例子

mosquitto_sub

基于订阅发布的

http://mosquitto.org/download/MQTT服务端程序

订阅消息

mosquitto_sub -h 127.0.0.1 -t debug(主题)

发布消息

mosquitto_pub -h 127.0.0.1 -t debug -m "Hello World" -r(保留在服务器上最后一条信息功能)

node-red

增加/test主题

mqtt.fx

订阅/test

接收消息

发布消息

1.5 批量扫描设备

fofa搜索1883端口(port="1883" && protocol="mqtt")

python扫描

扫描结果

2. 安全问题

1. MITM攻击

MQTT默认基于1883端口,由于认证信息默认明文传输,容易收到中间人攻击。可以使用SSL加密,其默认端口为8883。

2. 未授权问题

MQTT消息服务器存在配置错误,使用者没有配置认证造成未授权访问。

  • 利用通配符获取订阅所有Tomqtt

MQTT主题支持"+,#"的通配符,假设我们有两个Tomqtt分别为CMD/123/456 CMD/789/666,那么我们可以订阅CMD/#来获取CMD下的全部消息。在攻击中我们首先就可以利用,监听所有的不易开头的。对于开头的Tomqtt。对于​开头的Tomqtt我们可以使用$SYS/#来订阅

https://github.com/mqtt/mqtt.github.io/wiki/SYS-Tomqtts

  • 权限控制问题

MQTT服务器没有对普通用户权限进行管理,允许其使用通配符。

对用户没有权限验证导致其可以任意订阅内容,获取其他成员消息

比如说打开电灯、打开车门的类似操作

3. 密码爆破

匿名登录问题

import paho.mqtt.client as mqtt
​def on_connect(client, userdata, flags, rc):
print("[+] Connection successful")
client.subscribe('#', qos=1)
client.subscribe('$SYS/#')
​def on_message(client, userdata, msg):
print('[+] Tomqtt: %s - Message: %s' % (msg.tomqtt, msg.payload))
​# Press the green button in the gutter to run the script.
if __name__ == '__main__':
client = mqtt.Client(client_id="MqttClient")
client.on_connect = on_connect
client.on_message = on_message
client.connect('扫描设备IP', 1883, 60)
client.loop_forever()
# client.publish('/test', "I am from pycharm client!")
emqttd/etc/emq.conf # 默认开启是任何人都能登录需要改为false
mqtt.allow_anonymous = true

注意认证插件

EMQ中,emq_plugin_template为一个开发插件的模板,自动开启导致任意用户密码都可以连接成功。

暴力破解

开启认证模式

开启插件

https://github.com/zombiesam/joffrey

密码爆破

总结

MQTT协议作为物联网常用协议,带来技术便利的同时也产生了许多安全问题,需要开发者注意。

# Mqtt
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录