第1章:Modbus协议认证缺失漏洞
1.1 无认证机制原理
说实话,我第一次接触Modbus协议时,最让我震惊的就是——它居然没有任何认证机制。你想想看,一个在工业现场广泛使用了四十多年的协议,竟然连最基本的用户名密码都没有。
Modbus协议在设计之初,默认运行在可信的封闭网络环境中。那时候的工程师们压根没想过会有外部攻击这回事。所以协议帧里,根本没有预留任何认证字段。我给大家看一个典型的Modbus TCP请求帧结构:
事务标识符(2字节) | 协议标识符(2字节) | 长度(2字节) | 单元标识符(1字节) | 功能码(1字节) | 数据(n字节)
看到了吗?从头到尾,没有任何地方存放密码、令牌或者签名信息。这意味着什么?只要你能物理接入网络,就能直接操控任何Modbus设备。我在项目中遇到过不少客户,他们以为加个防火墙就万事大吉了。但防火墙只能过滤IP和端口,对Modbus协议本身的内容完全无能为力。
核心问题:Modbus协议假设所有通信方都是可信的。这个假设在今天的工业互联网环境下,已经完全不成立了。
1.2 中间人攻击演示
中间人攻击,说白了就是攻击者悄悄插到你和设备之间,你发给设备的指令他先看一眼,设备给你的回复他也先过一手。我习惯用Scapy这个Python库来做演示,因为它足够灵活。
先看一个最简单的中间人脚本框架:
from scapy.all import *
def modbus_mitm(packet):
# 检查是否是Modbus TCP包
if packet.haslayer(TCP) and packet[TCP].dport == 502:
print(f"捕获到Modbus请求: {packet.summary()}")
# 这里可以修改功能码或数据
# 比如把写线圈指令改成写多个寄存器
# 转发修改后的包
send(packet)
# 开始监听
sniff(filter="tcp port 502", prn=modbus_mitm)
嗯,这里要注意,实际攻击中攻击者还需要做ARP欺骗或者DNS劫持,才能让流量经过自己的机器。我曾经在实验室里复现过这个场景:攻击者把"停止传送带"的指令,悄悄改成了"启动传送带"。操作员在HMI上看到的是"已停止",但现场设备实际上在高速运转。
避坑指南:我曾经见过一个案例,某工厂的工程师用Wi-Fi连接PLC进行调试。攻击者只需要在同一个Wi-Fi网络下,就能轻松实施中间人攻击。所以,永远不要在非隔离网络上调试工业设备。
1.3 未授权访问风险
未授权访问,这个风险其实比中间人攻击更隐蔽。因为很多工程师觉得"反正没人知道我的IP地址",或者"我们用的是私有协议端口"。但现实是,攻击者用Shodan这样的搜索引擎,分分钟就能扫到暴露在公网上的Modbus设备。
我给大家列一下常见的未授权访问风险点:
- 功能码滥用:攻击者可以使用功能码0x01(读线圈)读取所有开关状态,用0x03(读保持寄存器)读取工艺参数
- 写操作风险:功能码0x05(写单个线圈)和0x06(写单个寄存器)可以直接修改设备运行状态
- 诊断信息泄露:功能码0x08(诊断)可以获取设备固件版本、运行时间等敏感信息
- 广播攻击:Modbus支持广播地址0x00,攻击者可以同时控制所有设备
我记得有一次帮某水处理厂做安全评估,发现他们的PLC暴露在公网上,任何人都可以用ModScan工具读取到所有水泵的启停状态。更可怕的是,攻击者可以直接发送写指令,把水泵全部关掉。你想想看,如果这是深夜,值班人员又没注意到报警...后果不堪设想。
个人经验:我建议所有使用Modbus协议的现场,至少要做到以下三点:
- 使用VPN或专用通道,不要让Modbus流量裸奔在公网上
- 在网关层做白名单过滤,只允许特定的功能码通过
- 部署工业防火墙,对Modbus协议深度解析,检测异常行为
说白了,Modbus协议就像一把没有锁的门。在三十年前,大家都不锁门也没事。但现在,你必须自己加把锁。下一章我会详细讲怎么给这把门加上锁——也就是具体的防护方案设计。
本章小结:
- Modbus协议设计时没有考虑认证,这是根本原因
- 中间人攻击可以实时篡改控制指令,且操作员难以察觉
- 未授权访问让攻击者可以远程操控设备,风险极高
- 防护思路:在协议层之上增加认证和加密机制
好,这一章就到这里。下一章我们聊聊具体的防护方案,包括如何用TLS给Modbus加层保护壳,以及我在实际项目中踩过的坑。