Modbus协议是一种用于工业控制系统中设备间通信的通用协议,它广泛应用于自动化领域。然而,正如许多其他通信协议一样,Modbus也存在安全漏洞,这使得它容易受到恶意攻击。当前有很多Scada软件都通过modbus协议来对PLC进行控制与管理。
Modbus协议由于没有认证的环节,再加上现在很少有将modbus进行加密通信的,所以modbus在内网里面基本上都是透明的,只要控制内网的某个控制器,就可以进行重放modbus协议攻击。目前都是modbus协议攻击都是以破坏的目的为主。根据modbus协议攻击的方法,大致分为以下3种:
中间人攻击:黑客可以截取Modbus通信流量,并修改数据或注入虚假命令,从而干扰设备的正常运行。
拒绝服务攻击(DoS):通过发送大量无效请求或者使设备超载,攻击者可以使Modbus从站无法正常工作,导致服务停止。
协议重放:攻击者可以利用Modbus协议的漏洞,向设备发送恶意指令,例如修改参数、篡改数据或者执行不当操作。还可能通过读请求获取泄露敏感信息,例如设备配置、生产数据等,为攻击者提供可利用的情报。
其中中间人攻击、协议重放的原理大致相同,都是通过修改modbus的功能函数与寄存器的值来达到读写数据,拒绝服务攻击则是大量发送正常或是异常modbus请求,让modbus从机瘫痪。
Modbus主从机通讯分为串口通讯(RTU和ASCII)与TCP通讯,目前Modbus攻击通常都是以TCP为主,所以这里只分析tcp协议。Modbus协议解析相关的内容各大社区都可以获取得到,这里不做详细说明,只说明几个站在攻击角度比较关键的点。
首先是modbus协议的交互方式
Modbus协议发送slave的时候,如果slave解析为合法协议,就给与一个相应的回复,如果不合法,那么就不会响应
然后是协议结构
协议样例:
00 00 00 00 06 01 05 00 1a ff 00(无校验位)
其中比较重要的位置就是在于站号、功能码、寄存器地址、寄存器值这四个位置。
站号是slave(Modbus从站)的编号,通常可以利用爆破来获取编号,如果站号不对,slave通常不会有响应。
功能码对应的能力如下表
功能码 | 描述 | PLC地址 | 寄存器地址 | 位/字操作 | 操作数量 |
01H | 读线圈寄存器 | 00001-09999 | 0000H-FFFFH | 位操作 | 单个或多个 |
02H | 读离散输入寄存器 | 10001-19999 | 0000H-FFFFH | 位操作 | 单个或多个 |
03H | 读保持寄存器 | 40001-49999 | 0000H-FFFFH | 字操作 | 单个或多个 |
04H | 读输入寄存器 | 30001-39999 | 0000H-FFFFH | 字操作 | 单个或多个 |
05H | 写单个线圈寄存器 | 00001-09999 | 0000H-FFFFH | 位操作 | 单个 |
06H | 写单个保持寄存器 | 40001-49999 | 0000H-FFFFH | 字操作 | 单个 |
0FH | 写多个线圈寄存器 | 00001-09999 | 0000H-FFFFH | 位操作 | 多个 |
10H | 写多个保持寄存器 | 40001-49999 | 0000H-FFFFH | 字操作 | 多个 |
寄存器地址通俗一点的理解就是PLC的功能块,简单是说就是标识,例如搅拌机、电源开关之类的,指定寄存器地址的值可以对这个功能块进行操作。
值就是功能块的状态,例如电源开就是:00 00,电源关就是:FF 00。
本次攻击测试采用Matesploit与Modbus模拟器ModbusPal来进行,Matesploit在2012年就集成modbus相关攻击脚本。
用的比较多的就是auxiliary/scanner/scada/modbus_findunitid(站ID爆破)和auxiliary/scanner/scada/modbusclient(modbus功能操作)模块。
打开ModbusPal来模拟一个slave,添加一个coil(线圈)
模拟modbus slave之后,就可以开始攻击了,首先可以先爆破UNITID(站ID)
Msf这个脚本是单线程,爆破起来很慢,也可以写一个python进行多线程爆破
可以看到UNITID是233的时候有返回值。确定UNID为233。
再使用auxiliary/scanner/scada/modbusclient模块对modbus进行攻击,这里以修改Coil(线圈)的值为攻击点。
首先设置攻击点
Set action WRITE_COIL为修改单个Coil的值,带S代表修改多个Coil。设置如下图
其中DATA_ADDRESS为Coil起始地址,DATA_COIL为地址偏移量,DATA为值(0或1)
修改前
修改后
一般PLC的指令是以写线圈的请求下发的,Modbus协议重放攻击篡改PLC基本都是以篡改Coil为主。
传统的IDS对工控攻击协议的解析能力不是特别强,例如snort,虽然支持对modbus的协议进行解析,但是使用的是预处理机制的,在原理上还是还是使用的特征匹配,虽然能够进行威胁识别,但是灵活度不高,不止snort,其他传统的特征匹配型的IDS均存在这个问题,所以建议使用zeek来进行工控类别的威胁检测。
Zeek(以前称为Bro)是一种开源的网络安全监控系统和网络流量分析框架。它最初是由加州大学伯克利分校开发的,旨在帮助网络管理员和安全专家实时监控和分析网络流量,以发现安全威胁、异常行为和网络性能问题。Zeek采用了一种基于网络流量分析的更为灵活的方法。它对网络流量进行深度分析,提取并聚合各种元数据,然后将这些元数据与用户定义的策略进行匹配,以检测潜在的威胁或异常活动。并且zeek的规则编写更加灵活,支持更多的插件。
工控安全漏洞库CISA为zeek量身打造了一个关于modbus的协议解析套件。项目地址:https://github.com/cisagov/icsnpp-modbus/
可以根据项目地址进行安装组件
可以利用这款套件对modbus攻击做检测,这里以暴力破解UNITID为例,DDOS攻击同样可以使用这种方法进行检测。
暴力破解以DOS类攻击都有相同的特点,就是发送大量的TCP包,但是传统的特征匹配型的IDS没有检测单位时间内包数量的功能,如果需要检测,只能编写插件,zeek自带了一个SumStats插件,可以统计数量(虽然很多人吐槽SumStats不好用,但是有总比没有好)
利用wireshark抓一下破解UNITID的包,输入"modbus"过滤一下modbus协议内容
发现这些包统一的func都是4,即读输入寄存器操作,且UNITID在进行遍历,访问都没有回包,是一个很明显的扫描操作。
我们可以参考icsnpp-modbus中的zeek规则文件提取出解析modbus的协议结构体加以利用。
这个record定义了modbus数据包的各个字典,由于是读输入寄存器操作,可以直接使用zeek自带的event:modbus_read_input_registers_request,相关的功能和定义可以参考zeek帮助
要识别扫描操作需要统计包中的所有读输入寄存器操作,使用event:modbus_read_input_registers_request进行来过滤包,并加入SumStats模块进行请求数量统计,参考icsnpp-modbus的event,我们可以编写如下功能
这个地方的if判断可以不用,因为这个event已经对流量进行过滤了。
然后使用SumStats::observe进行统计。SumStats需要两个比较关键的参数,一个是单位时间,一个是阈值,含义就是设置的单位时间内如果请求数量如果超过阈值,那么就判定为扫描攻击。我这里设置的是10秒发送5个包以上即判断为扫描攻击。
由于调用的SumStats插件代码过滤复杂,其中的细节不做赘述,有兴趣的同学可以参考detect-bruteforcing.zeek来进行编辑。
规则测试结果:
我将告警存放到了notice.log文件
总的来说,zeek检测modbus协议的方案已经比较成熟,可以很好的检测到modbus的威胁。