The Modbus preprocessor is a Snort module that decodes the Modbus protocol. It also provides rule options to access certain protocol fields. This allows a user to write rules for Modbus packets without decoding the protocol with a series of “content” and “byte_test” options.
Modbus is a protocol used in SCADA networks. If your network does not contain any Modbus-enabled devices, we recommend leaving this preprocessor turned off.
The Stream5 preprocessor must be enabled for the Modbus preprocessor to work. Protocol-Aware Flushing (PAF) is also required. See README.stream5 for more information.
Modbus configuration is split into two parts: the preprocessor config, and the rule options. The preprocessor config starts with:
preprocesor modbus:
Options are as follows:
Option Argument Required Default
--------------------------------------------------------------
ports <number>, or NO ports 502
{ port [port] ... }
Option explanations ports This sets the port numbers on which Modbus traffic is inspected. A single port number may be provided, or a space-separated list enclosed in curly brackets. The default is port 502.
Example preprocessor config
preprocessor modbus: ports { 502 }
The Modbus preprocessor adds 3 new rule options. These rule options match on various pieces of the Modbus headers.
The preprocessor must be enabled for these rule options to work.
This option matches against the Function Code inside of a Modbus Application-Layer request/response header. The code may be a number (in decimal format), or a string from the list provided below.
Syntax:
modbus_func:
code = 0-255
read_coils
read_discrete_inputs
read_holding_registers
read_input_registers
write_single_coil
write_single_register
read_exception_status
diagnostics
get_comm_event_counter
get_comm_event_log
write_multiple_coils
write_multiple_registers
report_slave_id
read_file_record
write_file_record
mask_write_register
read_write_multiple_registers
read_fifo_queue
encapsulated_interface_transport
Example: alert tcp any any -> any 502 (msg:”Modbus Write Coils request”; \ modbus_func:write_multiple_coils; sid:1;)
This rule option matches against the Unit ID field in a Modbus header.
Syntax:
modbus_unit:
unit = 0-255
Example: var MODBUS_ADMIN 192.168.1.2 alert tcp !$MODBUS_ADMIN any -> any 502 (msg:”Modbus command to Unit 01 \ from unauthorized host”; modbus_unit:1; sid:1;)
This rule option sets the cursor at the beginning of the Data field in a Modbus request/response.
Syntax: modbus_data;
No options.
Example:
alert tcp any any -> any any (msg:”String ‘badstuff’ in Modbus message”; \ modbus_data; content:”badstuff”; sid:1;)
The Modbus preprocessor uses GID 144 for its preprocessor events.
1 The length in the Modbus header does not match the length needed by the Modbus function code.
Each Modbus function has an expected format for requests and responses.
If the length of the message does not match the expected format, this
alert is generated.
2 Modbus protocol ID is non-zero. The protocol ID field is used for multiplexing other protocols with Modbus. Since the preprocessor cannot handle these other protocols, this alert is generated instead.
3 Reserved Modbus function code in use.