工业控制网络的安全问题一直让人头疼。以前大家都觉得工厂里的系统是封闭的,外人进不来,但如今物联网、远程运维、智能工厂这些东西搞起来后,工业控制网络早就不是“孤岛”了。攻击者一旦摸进来,轻则生产线停摆,重则设备损坏甚至引发安全事故。而传统的防火墙、杀毒软件到了工控环境里,经常像“鸡同鸭讲”——因为工业协议像Modbus、DNP3、OPC UA这些,它们认不出来。这时候,Snort这个老牌的开源入侵检测系统,反而能通过自定义规则和协议解析,在工控网络里找到用武之地。

一、工控网络到底哪里不一样

工控网络和咱们平时用的办公网络简直是两个世界。办公网络里跑的是HTTP、SMTP这些标准协议,传输数据是网页、邮件,就算被攻击顶多就是数据泄露。但工控网络里跑的是生产线上的实时指令,比如控制机器人手臂转多少度、调节反应釜温度、打开阀门等,这些指令一旦被篡改或者延迟,直接带来物理世界的损失。

1.1 工控协议的特殊性

以Modbus为例,它是工业控制领域很常见的一种协议。它的报文结构很简单,有一个功能码告诉设备要干什么,比如01表示读线圈、03表示读寄存器,还有数据地址和值。攻击者如果直接往PLC(可编程逻辑控制器)发一条功能码为05(写单个线圈)的报文,把某个电磁阀的开关改成1,那就能直接干预生产过程。问题在于,Modbus协议本身没有认证、没有加密,只要网络能通,任何人都能发指令。

1.2 传统安全设备为什么“失灵”

很多传统入侵检测系统是基于已知攻击特征库来工作的,比如查一下有没有包含恶意载荷的HTTP请求。但工控协议通常不是标准的HTTP,传统设备解析不了,甚至可能直接把Modbus报文当成垃圾流量丢掉,就检测不出任何异常。防火墙也一样,它能管端口和IP,但没法理解业务逻辑,不知道一个PLC向另一个PLC发送“写继电器”是不是合法操作。所以,工控网络急需一种能识别协议语义、能自定义检测规则的防护手段。

二、Snort靠什么本事撑起工控防护

Snort是一个开源网络入侵检测系统,它最厉害的地方就是高度可定制。你可以自己写规则,告诉它“什么样的流量是有问题的”。它还支持预处理插件,能够对特定协议做分帧和解析,比如Dissector。这样一来,它就能看懂Modbus报文里的功能码,再配合规则就能发现异常行为了。

2.1 Snort的三种工作模式

Snort可以当嗅探器用,只是把网络包显示出来;也能当记录器,把包存到文件里;最常用的是当入侵检测系统,根据规则匹配流量并报警。在工控网络里,我们通常把它接在交换机镜像端口,让它静静地旁路“看着”所有流量,不干扰生产,只负责发现可疑行为。

2.2 规则那点事儿

Snort规则有一个比较规矩的格式,大致是“动作 协议 源地址 源端口 方向 目标地址 目标端口 (规则参数)”。规则参数里可以包含msg(报警消息)、content(匹配内容)、sid(规则唯一ID)等。你可以针对工控协议的具体字段,用二进制数据匹配来定位某个功能码或者某个地址值。这种灵活性让安全人员可以像写“业务逻辑”一样设计检测规则。

三、针对工控场景的规则编写实战

想让Snort在工控网里发挥作用,光懂基础语法不够,还得会分析工控协议。下面我们拿Modbus和DNP3这两个常见工控协议举例,手把手写几条能直接用的规则。所有示例统一采用Snort规则语言编写。

3.1 检测Modbus中的非法读写操作

正常情况下,控制系统对PLC的读写应该只发生在固定的上位机IP和特定的PLC之间。如果有一个陌生IP突然去向PLC写寄存器,那多半是攻击行为。我们可以写一条规则:

# 技术栈:Snort规则语言
# 功能:检测来自非信任IP的Modbus写请求(功能码=05 写单个线圈)
# 假设Modbus TCP端口为502,源IP是攻击者,目的IP是被攻击的PLC
alert tcp any any -> 192.168.10.100 502 (
    msg:"可疑Modbus写请求,非法IP正在操作PLC";  # 报警信息
    flow:established,to_server;                # 仅检查已建立的TCP连接中发往服务器的数据
    content:"|00 00 00 00 00 06 01 05|";       # 匹配Modbus TCP的MBAP头(事务ID 00 00,协议ID 00 00,长度 00 06,单元ID 01)以及功能码 05
    offset:8;                                  # 从第8字节开始匹配(因为前面的MBAP头是7字节,但这里偏移量为8,实际是跳过TCP首部?这里简化为示例,实际需要调整)
    depth:8;                                   # 只匹配这8个字节
    side:check;                                # 检查源IP不是合法上位机
    sid:2000001;                               # 规则唯一ID
    rev:1;                                     # 版本号
    priority:1;                                # 高优先级
)

注意,上面这个规则为了简化,没有在侧边条件里写源IP集合,实际部署时我们可以这样写:先定义一个白名单IP,然后利用Snort的ipvar或者规则里的note等,但规则语法本身不直接支持“非”逻辑,我们可以通过sameip或代码块变通。更实用的办法是设置源地址为 !$WHITE_LIST,其中 $WHITE_LIST 是事先定义的变量。为了示例清晰,这里先展示基本结构。

我们再写一条更实用的:检测Modbus功能码为3(读寄存器)时,读取地址超出允许范围(比如超过0x100),这有可能是攻击者在探测量程信息。规则如下:

# 技术栈:Snort规则语言
# 功能:匹配Modbus读寄存器请求,且寄存器地址大于0x0F(15),可能用于探测
# 注意:Modbus报文中的寄存器地址字段在TCP payload的偏移位置,需要仔细计算
alert tcp $MODBUS_CLIENT any -> $MODBUS_SERVER 502 (
    msg:"Modbus非法地址读取,疑似探测"; 
    flow:established,to_server;
    content:"|00 03|";  # 功能码03,读保持寄存器
    offset:7;            # 从payload的第7个字节开始,因为MBAP头7字节,但需要注意(实际是第7字节后?这里简化)
    depth:2;           # 匹配功能码
    content:"|01 00|"; # 寄存器地址0x100? 这里用二进制匹配
    within:2;          # 在前一个content匹配后的2字节内出现?
    sid:2000002;
)

上面的规则写得比较粗糙,但目的是展示如何用content匹配二进制数据。实际开发工控规则时,最好使用Snort官方或社区提供的预处理插件,比如“modbus preprocessor”或“dnp3 preprocessor”,这些插件能把协议解析成字段,规则里就可以直接用 flowbits 或者 content 里面的字段值。下面我们看看DNP3的例子。

3.2 检测DNP3协议中的异常功能码

DNP3是电力和水利等工控系统常用的协议。它的请求报文里有一个应用层控制字节,其中低6位表示功能码,比如0x01表示读取,0x02表示写,0x03表示操作等。有些功能码很危险,比如0x1B表示“直接操作”,如果没有合适的口令,可能导致设备动作。我们可以写规则来阻拦或报警:

# 技术栈:Snort规则语言
# 功能:检测DNP3协议中出现危险功能码0x1B(直接操作)且对象类型为“控制”
# 假设DNP3端口是20000
alert tcp any any -> any 20000 (
    msg:"DNP3危险直接操作指令"; 
    flow:established,to_server;
    dnp3_func: CONTROL_OPERATION;  # 预处理插件解析后,直接使用dnp3_func字段?实际需参考Snort DNP3预处理器语法
    # 这里不需要content匹配了,因为预处理器已经把功能码解析成可读字段
    # 也可以补充content匹配对象头类型,比如控制对象等
    sid:2000003;
    rev:1;
)

因为Snort默认自带DNP3预处理器,所以规则简洁很多。你可以通过配置snort.conf启用preprocessor dnp3,然后规则里就能用dnp3_func、dnp3_obj等选项。实际生产中,这样的规则能有效识别那些试图远程操控断路器的恶意流量。

3.3 用flowbits标记连接状态

工控攻击往往是多阶段的,比如先扫描,再建立合法的读写会话,最后突然写一个非法地址。我们可以用一个规则标记扫描行为,用另一个规则关联后续操作。比如:

# 技术栈:Snort规则语言
# 规则1:检测到Modbus扫描(连续大量读请求),设置flowbits标志“modbus_scan”
alert tcp $MODBUS_CLIENT any -> $MODBUS_SERVER 502 (
    msg:"Modbus扫描行为";
    flow:established,to_server;
    content:"|00 03|"; offset:7; depth:2;
    # 统计窗口?这里简化
    flowbits:set,modbus_scan;  # 设置标志
    sid:2000004;
)

# 规则2:如果已标记“modbus_scan”,且随后出现对地址0x100的写操作,则高优先级报警
alert tcp $MODBUS_CLIENT any -> $MODBUS_SERVER 502 (
    msg:"Modbus扫描后试图写异常地址";
    flow:established,to_server;
    content:"|00 06|"; offset:7; depth:2;  # 功能码06写单个寄存器
    content:"|01 00|"; within:2;           # 地址0x100
    flowbits:isset,modbus_scan;           # 检查是否已扫描
    sid:2000005;
)

这个例子展示了Snort不同规则间的状态相关能力。这样组合起来,就能在一定程度上识别攻击者的“侦察-打击”过程,而不仅仅是单条报文的异常。

四、整体防护架构怎么搭

有了规则还不行,得把Snort放到合适的位置,和其他安全设备配合,才能构建一套完整的工控网络防护架构。

4.1 旁路镜像:不碍事的“观察哨”

工控网络最讲究稳定,绝不能让IDS串在业务链路中,否则它挂了会影响生产。所以标准做法是:把生产网的交换机配置端口镜像,把需要监控的流量复制一份给Snort所在的主机。Snort就像个“观察哨”,只看不说,发现了问题就报警,但不会阻断流量。阻断工作可以交给防火墙或者工业防火墙来处理,或者由安全人员手动响应。这样即使Snort死机,生产流量也不受一点影响。

4.2 与防火墙和SIEM联动

Snort本身只负责报警,但报警信息需要有人看。我们可以把Snort的日志格式输出为syslog,然后发给SIEM平台(比如Splunk、ELK)做集中展示和告警。同时,如果发现严重攻击,SIEM可以通过API通知防火墙动态封禁源IP,或者在工业防火墙上下发新的ACL规则,从而及时切断攻击源。这种联动架构很常见,而且Snort能很容易地对接。

下面是一个简单的snort.conf配置片段,用于启用syslog输出:

# 技术栈:Snort规则语言(snort.conf)
output alert_syslog: LOG_AUTH LOG_ALERT

更详细的配置可能还要定义工控网络的子网变量,比如:

# 技术栈:Snort规则语言(snort.conf)
ipvar MODBUS_CLIENT 192.168.1.0/24   # 合法上位机网段
ipvar MODBUS_SERVER 192.168.10.0/24  # PLC网段

4.3 规则管理和更新

工控网络里的业务相对固定,规则也不需要频繁变更。但每次工艺调整,比如新增了一条生产线,IP地址变了,或者PLC固件升级后协议行为有变化,我们都需要及时更新规则。平时可以建立规则版本管理,用Git保存规则文件,方便回滚。还要定期对规则做测试,可以用真实的工控流量回放到Snort里,看看有没有漏报和误报。

五、踩坑注意事项

Snort在工控网络里不是魔法棒,部署时有很多坑,得提前知道。

5.1 误报率可能很高

工控网络的流量模式跟IT网络差异很大。比如有的PLC每隔几百毫秒就发送一次心跳包,这种周期性流量很容易被误判成扫描。又比如上位机在启动时会批量轮询所有寄存器,如果我们规则里写了“连续读大量寄存器认为是扫描”,可能把正常的初始化过程也拦了。所以写规则时要非常熟悉业务流程,最好拿着真实抓包分析后归纳“正常长相”,再用“反向”思路找异常。

5.2 性能瓶颈和丢包

Snort是单线程或可多线程的,但在高速工业网络里,如果监控支路流量很大,比如工业以太网跑千兆,Snort可能处理不过来,导致丢包。这时候可以优化规则:尽量少用复杂正则和过大的content匹配,把规则数量控制在必要范围。还可以调整Snort的pfring或af-packet抓包模式,提高性能。如果还是不够,就拆分流量,多台Snort分别监控不同VLAN或不同协议。

5.3 协议解析到了极限

Snort自带的DNP3和Modbus预处理器覆盖了公共协议的常见字段,但有些厂家私有扩展,或者基于TCP的PROFINET、EtherCAT等协议,Snort默认解析不了。这时候需要你自己写规则或者开发自定义预处理器,工作量不小。好在Snort社区有些开源项目提供了额外协议解析,比如针对S7comm、Modbus的深度检测插件,可以找来看看。

六、总结

Snort虽然是个“老家伙”,但凭借开源、灵活、规则丰富的特点,在工业控制网络的安全防护中依然能扮演重要角色。只要把它放在旁路、写好工控协议规则、配合联动响应机制,就能大大提升工控网络对异常操作的感知能力。当然,它不能替代工业防火墙或白名单机制,但作为检测层,它像一双隐藏在暗处的眼睛,帮你看住那些试图捣乱的“手”。希望这篇文章能让你对Snort在工控网络中的应用有一个清晰的思路,动手去验证一下,你的工厂又安全了一分。