With more devices on operational technology (OT) networks now getting connected to wide-reaching IT networks, it is more important than ever to have effective detection capabilities for ICS protocols.

However, there are a few issues that usually arise when creating detection for ICS protocol traffic.

Oftentimes, the protocols connecting these devices on modern networks originate in older serial protocols. This transition resulted in protocols that use techniques like bitfields to reduce message size and multiple levels of encapsulation to avoid changes to the original protocol. These protocols often support combining multiple requests into one packet (pipelining) or splitting up a single request across multiple packets (fragmenting). Snort is fully capable of detecting traffic using any of these approaches, however, it requires a deeper understanding of the underlying protocol and more complicated plaintext rules, which is not always feasible.

The solution to these problems lies in the use of a Snort 3 service inspector for protocols requiring increased detection capabilities. Service inspectors are an evolution of Snort 2's preprocessors, providing access to additional built-in rules that look for protocol-level abnormalities, normalize pipelined and fragmented messages, and provide additional verification that the traffic being inspected is the expected protocol. Through the use of rule options exposed by existing service inspectors, plaintext rule writers can focus on the coverage of interest and let Snort handle protocol decoding and normalization.

Protocols

At this time Snort 3 contains six service inspectors focused on ICS protocols: MMS, IEC104, ENIP/CIP, S7CommPlus, DNP3 and Modbus.

MMS

Compatibility

Snort 2

-

Snort 3

>= 3.1.45.0

Cisco Talos released a dedicated Snort 3 service inspector for the Manufacturing Message Specification (MMS) protocol defined within IEC 61850 in Spring 2022 with the release of Firepower 7.2. Without a service inspector, creating any kind of coverage for MMS was complicated and error-prone. Every individual rule required the writer to parse the various protocol layers that an MMS PDU is encapsulated in before any processing of the target bytes could take place. Rule writers needed to account for the multitude of ways that these encapsulation layers could be changed around while still delivering the same PDU. Even in cases where a rule writer took the time to write coverage for every combination, message pipelining techniques could often evade even the most thought-out rules. In acknowledgment of these difficulties and requests for better support for the protocol, we decided a service inspector for MMS was the best path forward. With the dedicated service inspector, a rule writer often no longer needs to be concerned with any of the encapsulation layers or the usage of pipelining techniques. Instead, they can be written exclusively on the MMS PDU, leaving the parsing and normalization to the inspector.

With a large amount of identifiable information within a fully encapsulated MMS message, it was a prime candidate for the use of the Snort 3 wizard framework. By using a Curse to fingerprint MMS traffic, we gain a few advantages. First, the detection is no longer bound to a specific port, allowing for detection to occur by default even on non-standard implementations. This provides an additional advantage of avoiding port conflicts when multiple protocols that typically operate on the same port, like MMS and S7CommPlus, are in use together. It also allows for a reduced number of false-positive alerts, as traffic is more closely verified.

This inspector introduces two plaintext rule options, simplifying the amount of work required to create custom MMS rules. When used in conjunction with the ber_data and ber_skip rule options, it is possible to create coverage for specific fields of an MMS PDU without worrying about message encapsulation. The first rule option, mms_func, checks to see if any of the supported MMS Confirmed-Request or MMS Confirmed-Response services are present within a given TCP stream. The second, mms_data, moves the detection cursor to the start of the MMS PDU, allowing rule writers to start detection from a consistent location.

An example rule showing how the functionality introduced by this inspector can be used is shown below. In this case, the rule is searching for the existence of an abnormal value in the acceptableDelay field of an MMS Take Control Confirmed Service Request.

alert mms ( \
  msg:"PROTOCOL-SCADA MMS take_control abnormal acceptableDelay value"; \
  flow:to_server,established; \
    
  # fast pattern
  content:"|A0|"; \
    
  # only enter in the takeControl confirmed service request
  mms_func:take_control; \
    
  # move cursor to the start of the mms data
  mms_data; \
    
  # enter the confirmed-RequestPDU
  ber_data:0xA0; \
    
  # skip `invokeID`
  ber_skip:0x02; \
    
  # enter the `takeControl` confirmed service request
  ber_data:0xB3; \
    
  # enter the `takeControl` data
  ber_data:0xA0; \
    
  # skip the semaphore name
  ber_skip:0x80; \
    
  # skip the named token parameter if it exists
  ber_skip:0x81,optional; \
    
  # skip the priority parameter if it exists
  ber_skip:0x82,optional; \
    
  # enter the `acceptableDelay` parameter
  ber_data:0x83; \
    
  # match on the abnormal value
  content:"|FF FF|", within 2; \

  classtype:protocol-command-decode; \
  sid:1000000; \
)

Talos analysts have written a set of rules to identify MMS traffic. These rules are intended exclusively to indicate the presence of particular messages — they are not indicative of malicious traffic on their own. Coverage can be found with the following SIDs: 45423 - 45436 and 50538 - 50615.

Additional information about the usage and features of the MMS service inspector can be found here.

Additional information on the BER rule options can be found here.

IEC104

Compatibility

Snort 2

-

Snort 3

>= 3.1.13.0

Cisco Talos released a dedicated Snort 3 service inspector for IEC 60870-5-104 (IEC104) in Spring 2021 with the release of Firepower 7.0. Without a service inspector, creating coverage for IEC104 traffic was achievable, but involved. The protocol uses a bit field in its header to determine which type of message is to follow. While Snort is fully capable of parsing this bit field, it can get tedious to need to do so for every rule. Even in cases where a rule writer took the time to correctly parse the message, pipelining techniques could still be used to evade detection. To allow Talos to provide more complete coverage, and to better assist rule writers, we decided that building a service inspector for IEC104 was the best move.

The IEC104 inspector additionally includes 54 rules built into the inspector itself that alert on abnormal and invalid usage of the protocol. These rules do not inherently indicate that a stream contains malicious traffic — they are intended to serve as an indicator that some of the traffic may deviate from the specification. Built-in rules can be enabled or disabled via the enable_builtin_rules boolean configuration entry.

Due to the small amount of uniquely identifiable information within an IEC104 message, a port binding was chosen instead of Snort 3 wizard support. By default, this inspector is bound to TCP/2404.

This inspector introduces two plaintext rule options, simplifying the amount of work required to create custom rules. The first rule option iec104_apci_type allows the rule writer to easily determine which of the three Application Protocol Control Information (APCI) types are specified by the current message. The second, iec104_asdu_func, checks to see if the message being inspected matches the Application Service Data Unit (ASDU) function specified.

An example rule showing how the functionality introduced by this inspector can be used is shown below. In this case, the rule uses the iec104_asdu_func rule option to search for a C_IC_NA_1 request containing a specific address value in its address field.

alert iec104 ( \
  msg:"PROTOCOL-SCADA IEC104 C_IC_NA_1 abnormal address"; \
  flow:to_server,established; \
    
  # verify the ASDU Type ID matches the target interrogation command
  iec104_asdu_func:C_IC_NA_1; \
    
  # check to see if the Address field matches the target value
  content:"|BE EF|", offset 10, depth 2; \
  
  classtype:protocol-command-decode; \
  sid:1000000; \
)

Alternatively, if the goal is to simply alert whenever a Numbered Supervisory Function request crosses the sensor, the iec104_apci_type rule option can be used as shown in the rule below:

alert iec104 ( \
  msg:"PROTOCOL-SCADA IEC104 Numbered Supervisory Function request"; \
  flow:to_server,established; \
    
  # fast pattern on the magic
  content:"|68|", depth 1; \
    
  # verify the Numbered Supervisory Function (S) APCI type is in use
  iec104_apci_type:S; \
  
  classtype:protocol-command-decode; \
  sid:1000000; \
)

Talos analysts released a set of rules to identify IEC104 traffic. These rules are intended exclusively to indicate the presence of particular messages — they are not indicative of malicious traffic on their own. Coverage can be found with the following SIDs: 41047 - 41052, 41078, 41079 and 52150 - 52203.

Additional information about the usage and features of the IEC104 service inspector can be found here.

CIP & EtherNet/IP

Compatibility

Snort 2

-

Snort 3

>= 3.1.51.0

Jian Wu of Cisco released a dedicated Snort 3 service inspector for the Common Industrial Protocol (CIP) and EtherNet/IP (ENIP) protocols in 2019. Creating coverage for either of these protocols is no small task given the multitude of available options, functions, and sub-protocols. The CIP service inspector assists rule writers by exposing a few of the more commonly needed fields through rule options. By using the service inspector instead of manually parsing the entire protocol, rule writers gain the added benefit of normalized message buffers, reducing the chances of missed detection due to pipelining techniques.

The CIP inspector additionally includes four rules built into the inspector itself that alert on abnormal and invalid usage of the protocol. These rules do not inherently indicate that a stream contains malicious traffic — they are intended to serve as an indicator that some of the traffic may deviate from the specification. Built-in rules can be enabled or disabled via the enable_builtin_rules boolean configuration entry.

At this time, there is no wizard support for the CIP service inspector. By default, it is bound to TCP/44818 and UDP/22222. This can be modified as needed in the Snort configuration.

This inspector introduces eleven plaintext rule options, simplifying the amount of work required to create custom rules. Four rule options (cip_req, cip_rsp, enip_req, and enip_rsp) are used to quickly verify the direction of the transaction. Six of these (cip_attribute, cip_class, cip_conn_path_class, cip_instance, cip_service, and cip_status) allow rule writers to alert on specific fields within the protocol's encapsulation headers. Lastly, one rule option, enip_command, allows rule writers to alert on a specific EtherNet/IP command, specified by its integer value.

An example of a rule using a combination of the available CIP service inspector rule options can be found below. In this example the rule is looking for a PCCC request contained within an ENIP Send RR Data request.

alert cip (
  msg:"PROTOCOL-SCADA EtherNet/IP PCCC request"; \
  flow:to_server, established; \
    
  # fast pattern on the ENIP command code
  content:"|6F 00|", depth 2; \
    
  # check for a Send RR Data ENIP command
  enip_command:0x6F; \
    
  # check for the PCCC CIP service code
  cip_service:0x4B; \
 
  classtype:protocol-command-decode; \
  sid:1000000; \
)

Additional information about the usage and features of the CIP service inspector can be found here.

S7CommPlus

Compatibility

Snort 2

>= 2.9.20

Snort 3

>= 3.1.45.0

Pradeep Damodharan of Cisco released a dedicated Snort 3 service inspector for the S7CommPlus protocol in 2019. As the data section of a S7CommPlus PDU is encrypted, detection content is focused on the plaintext header. This inspector focuses on normalizing the traffic and decreasing the complexity for rule writers by exposing a few rule options for common functionality.

The protocol supported by this service inspector should not be confused with the similarly named S7Comm.

The S7CommPlus inspector includes three rules built into the inspector itself that alert on abnormal and invalid usage of the protocol. These rules do not inherently indicate that a stream contains malicious traffic — they are intended to serve as an indicator that some of the traffic may deviate from the specification. Built-in rules can be enabled or disabled via the enable_builtin_rules boolean configuration entry.

With the amount of identifiable information contained within the S7CommPlus header, it made sense to use the Snort 3 wizard framework for protocol identification. By using a curse to fingerprint S7CommPlus traffic we gain a few advantages: The detection is no longer bound to a specific port, allowing for detection to occur by default even on non-standard implementations. This provides an additional advantage of avoiding port conflicts when multiple protocols that typically operate on the same port, like S7CommPlus and MMS, are in use together. It also allows for a reduced number of false-positive alerts, as traffic is more closely verified.

This inspector introduces three plaintext rule options, simplifying the amount of work required to create custom rules. The first rule option s7comm_content moves the detection cursor to the start of the PDU data, allowing rule writers to start detection from a consistent location. The second rule option s7comm_func checks to see if the traffic contains the specified function code (such as explore, createobject, deleteobject, etc.). The final rule option, s7comm_opcode, checks to see if the request type opcode matches the specified value (such as request, response, notification, etc.).

An example of using one of the inspector's rule options can be found below. In this example, the rule first checks if the message contains the bytes making up the target function and then uses the inspector to verify that the S7CommPlus CreateObject function is in use.

alert s7commplus (
  msg:"PROTOCOL-SCADA S7CommPlus CreateObject request"; \
  flow:to_server, established; \
    
  # fast pattern on CreateObject
  content:"|04 CA|"; \
    
  # check for the CreateObject function code
  s7commplus_func:createobject; \

  classtype:protocol-command-decode; \
  sid:1000000; \
)

Additional information about the usage and features of the S7CommPlus service inspector can be found here.

DNP3

Compatibility

Snort 2

>= 2.9.20

Snort 3

>= 3.1.20.0

Ryan Jordan, Rashmi Pitre, and Maya Dagon of Cisco released a dedicated Snort3 service inspector for Distributed Network Protocol 3 (DNP3) in 2015. When enabled, this inspector provides PDU normalization and anomaly detection for DNP3 traffic on TCP and UDP.

The DNP3 inspector includes six rules built into the inspector itself that alert on abnormal and invalid usage of the protocol. These rules do not inherently indicate that a stream contains malicious traffic — they are intended to serve as an indicator that some of the traffic may deviate from the specification. Built-in rules can be enabled or disabled via the enable_builtin_rules boolean configuration entry.

At this time, there is no wizard support or a default port binding for the DNP3 service inspector. To use the inspector, a DNP3 rule option must be used, the dnp3 service keyword must be specified, or a custom port binding must be added to the configuration. Additional information about configuring the Snort3 Wizard and Binder can be found here.

This inspector introduces four plaintext rule options, simplifying the work required to create custom rules. The first, dnp3_data, moves the detection cursor to the start of the PDU data, allowing rule writers to start detection from a consistent location. The dnp3_func checks if a PDU with the specified function code exists in the stream. Next, dnp3_ind is exposed to provide rule writers with easy access to the DNP3 indicator flags. Lastly, the dnp3_obj rule option exposes the protocol's object headers for access in plaintext rules.

An example of a rule using the DNP3 service inspector can be found below. In this example, the rule is looking for the use of the Disable Spontaneous Message function.

alert dnp3 (
    msg:"PROTOCOL-SCADA DNP3 Disable Spontaneous Message request"; \
    flow:to_server, established; \
    
    # fast pattern on magic
    content:"|05 64|", depth 2; \
    
    # check for the desired function code
    dnp3_func:disable_unsolicited; \

    classtype:protocol-command-decode; \
    sid:1000000; \
)

Additional information about the usage and features of the DNP3 service inspector can be found here.

Modbus

Compatibility

Snort 2

>= 2.9.20

Snort 3

>= 3.1.17.0

Russ Combs and Ryan Jordan of Cisco released a dedicated Snort3 service inspector for the Modbus TCP protocol in 2015.

The Modbus inspector includes three rules built into the inspector itself that alert on abnormal and invalid usage of the protocol. These rules do not inherently indicate that a stream contains malicious traffic — they are intended to serve as an indicator that some of the traffic may deviate from the specification. Built-in rules can be enabled or disabled via the enable_builtin_rules boolean configuration entry.

Currently, there is no wizard support for the Modbus service inspector. By default, it is bound to TCP/502. This can be modified as needed in the Snort configuration.

This inspector introduces three plaintext rule options, simplifying the work required to create custom rules. The first, modbus_data, moves the detection cursor to the start of the PDU data, allowing rule writers to start detection from a consistent location. Next, modbus_func allows rule writers to easily check to see if a specified Modbus function code is in use on traffic crossing the sensor. Lastly, modbus_unit exposes the MBAP header's Unit ID field for easy access in plaintext rules.

An example of a rule using the Modbus service inspector can be found below. In this example, the rule is looking for the existence of the Diagnostics request.

alert modbus (
  msg:"PROTOCOL-SCADA Modbus Diagnostics request"; \
  flow:to_server, established; \
    
  # fast pattern on null protocol identifier
  content:"|00 00|", offset 2, depth 2; \
    
  # check for the desired function code
  modbus_func:diagnostics; \
  
  classtype:protocol-command-decode; \
  sid:1000000; \
)

Additional information about the usage and features of the Modbus service inspector can be found here.

Resources

The service inspectors provided by Snort 3 allow for simplified plaintext rules while normalizing abnormal traffic to decrease false positives. More information on the existing ICS service inspectors and their rule options can be found at the resources below: