基于Peach的Modbus协议模糊测试

Fuzz简介

Fuzz是一种软件测试技术,其核心思想是将自动或者半自动生成的随机数据,输入到一个程序中,并监视程序异常,如崩溃、断言失败等,以发现程序漏洞。

根据测试数据生成的方式,可以将fuzz分为两种:

  • 基于生成的fuzz

  • 基于变异的fuzz

基于生成的fuzz需要了解程序的构成知识,比如要对协议fuzz的话,需要了解协议规范,字段含义等,来构建一个协议模型,使用模型来生成测试用例。基于生成的fuzz方法优点是构建的测试用例易于通过程序验证,能测试到更深此次的代码;缺点是在没有配置文件等先验知识的情况下,难以分析测试用例的格式。

基于变异的fuzz是根据已知的数据样本,通过对某个字段变异,来生成新的测试用例。比如可以打乱整个数据包、把数据包的某个部分替换掉。基于变异的fuzz关键思路是在数据包中放入大量随机数据。基于变异的模糊测试不需要对协议规约有很深的理解,其效率主要取决于测试样本的丰富程度以及所选用的变异算法,如果变异策略过于简单,会导致大部分测试数据被系统的浅层,逻辑所丢弃,达不到深入测试的目的。

一个基于变异的网络协议的Fuzz过程如下:

  1. 获得目标协议的正常数据包
  2. 用变异数据替换该数据包中的某些部分
  3. 用发包器向目标应用发包
  4. 观察目标应用的反应

在工控系统中,fuzz主要用来对工控设备使用的网络通信协议进行测试。工控网络通信协议可以分为两类:

  • 基础通信协议:ARP、IP、ICMP、TCP、UDP

  • 通用服务协议:HTTP、FTP、SNMP

对于基础通信协议和通用协议,可以用传统的fuzz工具,如Peach、Sulley进行测试,但由于工控协议大多私有,不同厂商使用自己的专有协议,导致传统的fuzz工具难以直接适用于工控系统,针对工控系统协议的fuzz可分为以下两种情况:

(1)标准公开的工控协议

一些已经公开标准的协议比如S7comm,可以采用基于生成发fuzz方法。可以根据协议规范,生成模型,通过对协议字段进行错误注入、对报文结构进行变异,对报文序列(标识上下文状态)进行变换来检测设备在处理异常时可能存在的漏洞。

(2)私有协议

对于未公开标准的私有协议,可以采用基于变异的fuzz方法。首先捕获私有协议正常的交互数据作为样本,在其基础上进行变异。

peach

peach支持基于生成和基于变异的fuzz方法,其特点是可扩展性,这运行它支持多种格式、网络协议、应用协议等,使其适用于Android设备、装置驱动程序以及嵌入式硬件的安全测试。

peach使用流程为:

  1. 创建数据、状态模型
  2. 选择/配置Publisher
  3. 配置代理、监视器
  4. 配置日志

优点:

  1. 支持多种协议和格式

  2. 高度可定制化

  3. 支持分布式测试,可以使用多台机器同时进行模糊测试,从而提高测试效率

  4. 支持状态跟踪,peach可以跟踪目标系统的状态变化,确保测试能覆盖到更多执行路径和状态

  5. 跨平台支持,peach支持Windows、Linux和macOS

缺点:

  1. peach更适合协议和格式的测试,不太适合GUI、游戏等测试

  2. 分布式测试对硬件资源要求较高

Modbus简介

Modbus是一个通用的总线通信协议,属于应用层协议。它不依赖于硬件总线,协议标准公开并支持多种电气接口,所以广泛应用于工业控制系统中。

Modbus是一主多从的通信协议,在Modbus通信中,只有一个主设备可以发送请求,其他从设备接收主设备的数据并响应,从设备可以是任何外围设备,比如I/O传感器、阀门、网络驱动器等。也就是说Modbus不能同步通信,总线上每次只有一个数据进行传输,即主设备发送,从设备应答;如果主设备不发送请求,总线上就没有数据传输。

Modbus通讯物理接口可以选用串口(RS232、RS485、RS422等),也可以选择以太网接口,对应的通信方式也有三种:

  • Modbus-TCP/IP

  • Modbus-RTU/Modbus-ASCII

  • Modbus-PLUS

Modbus存储区

Modbus协议规定了4个存储区,分别是0,1,2,3,可以通过读写寄存器访问。其中1、4区是可读可写的,1、3区是只读的。

  • 0区:输出线圈,可读可写的布尔变量,PLC地址范围为00001-09999,协议地址范围是0000H-FFFFH

  • 1区:输入线圈,只读布尔变量,PLC地址范围为10001-19999,协议地址范围是0000H-FFFFH

  • 3区:输入寄存器,只读寄存器,PLC地址范围为30001-39999,协议地址范围是0000H-FFFFH

  • 4区:保持寄存器,可读可写寄存器,PLC地址范围为40001-49999,协议地址范围是0000H-FFFFH

这里要着重注意一下PLC地址与协议地址的区别

PLC地址与协议地址

PLC地址是指存放于控制器中的地址,地址的第一位表示寄存器类型(1区、2区、3区、4区),协议地址是指通信时使用的寄存器地址,地址范围都是0000H-FFFFH,这里要主要的是,PLC地址0x00001对应的协议地址是0x0000,PLC地址0x10001对应的协议地址也是0x0000,3、4区也一样,不用担心在使用modbus读写寄存器时出错,因为访问不同寄存器的功能码不同,比如访问PLC地址0x00001使用的功能码是01,默认访问1区寄存器,所以就不存在寻址冲突啦。

Modbus TCP/IP 报文格式

Modbus TCP/IP报文格式如下图所示:

MBAP报头

  • 事务元标识符:2个字节,Modbus请求/响应事务处理的识别码

  • 协议识别符:2个字节,0000表示Modbus协议

  • 长度:2个字节

  • 单元标识符:1个字节,串行链路或其他总线上连接的远程从站的识别码

功能码

Modbus常用功能码如下,当主设备发起一个功能码为01的请求时,此时PDU结构为:

  • MBAP报头

  • 功能码:0x01(1个字节)

  • 起始地址:可选范围为0x0000~0xFFFF(2个字节)

  • 线圈数量:可选范围为1~2000(2个字节)

数据

数据域不确定,由具体的功能决定

Fuzz过程

搭建环境

使用MODSIM模拟modbus协议:

使用modbus数据采集工具modscan监测数据:

协议分析

由于Modbus主要用于读写寄存器,所以这里使用modscan修改某个寄存器的值,并使用wireshark抓包:

写寄存器的数据包如下,Modbus/TCP的报文为:9d 00 00 00 00 06 01 06 00 01 00 06,其中06是功能码,写寄存器,00 01是地址,00 06是写入目标寄存器的值,本次测试计划对写入寄存器的值进行变异。

pit文件编写

在安装好peach之后,将安装包中的模板文件复制一份,更名为modbus.xml,根据分析好的报文来修改模板文件。

DataModel

1
2
3
4
5
6
7
8
9
10
<DataModel name="send_data">
<Block name="mod_write_reg">
<Number name="01" size="16" value="9d 00" valueType="hex" signed="false" multable="false"/> <!--标识符-->
<Number name="02" size="16" value="00 00" valueType="hex" signed="false" multable="false"/> <!--标识符-->
<Number name="03" size="16" value="00 06" valueType="hex" signed="false" multable="false"/> <!--长度-->
<Number name="04" size="16" value="01 06" valueType="hex" signed="false" multable="false"/> <!--标识符和功能码(06)-->
<Number name="05" size="16" value="00 01" valueType="hex" signed="false" multable="false"/> <!--地址-->
<Number name="06" size="16" value="00 06" valueType="hex" signed="false" /> <!--数据-->
</Block>
</DataModel>

StateModel

StateModel定义了如何给目标发送和接收数据。

1
2
3
4
5
6
7
<StateModel name="TheState" initialState="Initial">
<State name="Initial">
<Action type="output">
<DataModel ref="send_data"/>
</Action>
</State>
</StateModel>

Agent

由于我使用的是modbus模拟器,所以在测试过程中无法实时监测设备状态,所以监视器选择Socket,对被测设备进行主动监听。

Socket是更精确级别的监听,可以查看端口是否存在,但Socket会使Fuzz测试速度变慢。

1
2
3
4
5
6
<Agent name="TheAgent">
<Monitor name="Socket">
<Param name="Host" value="172.29.152.130" />
<Param name="Port" value="502" />
</Monitor>
</Agent>

Test

Test用于配置一个指定的fuzzing测试,配置内容包括一个Publisher和其他选项组合成一个stateModel。

1
2
3
4
5
6
7
8
9
10
11
<Test name="Default">
<Agent ref="TheAgent"/>
<StateModel ref="TheState"/>
<Logger class="File">
<Param name="Path" value="logs"/>
</Logger>
<Publisher class="tcp.Tcp"> <!--指定通信方式为tcp-->
<Param name="Host" value="="172.29.152.1"/>
<Param name="Port" value="502"/>
</Publisher>
</Test>

Publisher

Publisher是Peach发送和接收数据的IO接口。

测试过程

运行peach.exe,fuzz正常运行:

关闭MODSIM之后,查看peach日志,可以看到peach检测出一个异常:

总结

这次主要是学习peach的用法,对modbus写寄存器功能的数据部分进行变异,看别人说如果只是发送合法的功能码,对数据部分进行编译会导致fuzz的时间非常长,所以要通过逆向固件先找到模糊测试点,再精确地进行fuzz,这块知识点要mark一下,后续继续学习。

参考链接

https://blog.csdn.net/as480133937/article/details/123197782[详解Modbus通信协议---清晰易懂-CSDN博客](https://blog.csdn.net/as480133937/article/details/123197782)

7.Peach 监听模块【工业控制系列】 - 奋斗的安卓勇士Blog

工控网络协议模糊测试:用peach对modbus协议进行模糊测试 - FreeBuf网络安全行业门户

工控安全研究:基于Peach的Modbus协议模糊测试 - 安全内参 | 决策者的网络安全知识库

工具:Peach_peach工具-CSDN博客