Giter Site home page Giter Site logo

cisagov / icsnpp-enip Goto Github PK

View Code? Open in Web Editor NEW
18.0 11.0 10.0 226 KB

Zeek Ethernet/IP and CIP Parser - CISA ICSNPP

License: BSD 3-Clause "New" or "Revised" License

CMake 0.19% Makefile 0.42% Shell 1.11% Zeek 51.61% C++ 2.95% JavaScript 43.05% Standard ML 0.67%

icsnpp-enip's Introduction

ICSNPP-ENIP

Industrial Control Systems Network Protocol Parsers (ICSNPP) - Ethernet/IP and CIP.

Overview

ICSNPP-ENIP is a Zeek plugin for parsing and logging fields within the Ethernet/IP protocol.

This plugin was developed to be fully customizable. To drill down into specific ENIP/CIP packets and log certain variables, users can add the logging functionality to scripts/icsnpp/enip/main.zeek. The functions within scripts/icsnpp/enip/main.zeek and src/events.bif are good guides for adding new logging functionality.

This parser produces four log files. These log files are defined in scripts/icsnpp/enip/main.zeek.

  • enip.log
  • cip.log
  • cip_io.log
  • cip_identity.log

For additional information on these log files, see the Logging Capabilities section below.

Installation

Package Manager

This script is available as a package for Zeek Package Manger

zkg refresh
zkg install icsnpp-enip

If this package is installed from ZKG, it will be added to the available plugins. This can be tested by running zeek -N. If installed correctly, users will see ICSNPP::ENIP.

If ZKG is configured to load packages (see @load packages in quickstart guide), this plugin and these scripts will automatically be loaded and ready to go. ZKG Quickstart Guide

If users are not using site/local.zeek or another site installation of Zeek and want to run this package on a packet capture, they can add icsnpp/enip to the command to run this plugin's scripts on the packet capture:

git clone https://github.com/cisagov/icsnpp-enip.git
zeek -Cr icsnpp-enip/tests/traces/enip_cip_example.pcap icsnpp/enip

Manual Install

To install this package manually, clone this repository and run the configure and make commands as shown below.

git clone https://github.com/cisagov/icsnpp-enip.git
cd icsnpp-enip/
./configure
make

If these commands succeed, users will end up with a newly created build directory. This contains all the files needed to run/test this plugin. The easiest way to test the parser is to point the ZEEK_PLUGIN_PATH environment variable to this build directory.

export ZEEK_PLUGIN_PATH=$PWD/build/
zeek -N # Ensure everything compiled correctly and you are able to see ICSNPP::ENIP

Once users have tested the functionality locally and it appears to have compiled correctly, they can install it system-wide:

sudo make install
unset ZEEK_PLUGIN_PATH
zeek -N # Ensure everything installed correctly and you are able to see ICSNPP::ENIP

To run this plugin in a site deployment, users will need to add the line @load icsnpp/enip to the site/local.zeek file to load this plugin's scripts.

If users are not using site/local.zeek or another site installation of Zeek and want to run this package on a packet capture, they can add icsnpp/enip to the command to run this plugin's scripts on the packet capture:

zeek -Cr icsnpp-enip/tests/traces/enip_cip_example.pcap icsnpp/enip

If users want to deploy this on an already existing Zeek implementation and don't want to build the plugin on the machine, they can extract the Zeek_Enip.tgz file to the directory of the established ZEEK_PLUGIN_PATH (default is ${ZEEK_INSTALLATION_DIR}/lib/zeek/plugins/).

tar xvzf build/Zeek_Enip.tgz -C $ZEEK_PLUGIN_PATH 

Logging Capabilities

It its default configuration, this parser will only log Ethernet/IP and CIP packets on ports 2222 and 44818. This decision was made due to the false positives generated when a signature-only based detection system was used.

If users know of Ethernet/IP and CIP traffic that operate on ports other than 2222 or 44818, there are two options:

  • Allow signature detection on additional, known ports only:
  • Allow signature detection on all ports (may produce false positive):

ENIP Header Log (enip.log)

Overview

This log captures Ethernet/IP header information for every Ethernet/IP packet and logs it to enip.log.

Fields Captured

Field Type Description
ts time Timestamp
uid string Unique ID for this connection
id conn_id Default Zeek connection info (IP addresses, ports)
is_orig bool True if the packet is sent from the originator
source_h address Source IP address (see Source and Destination Fields)
source_p port Source port (see Source and Destination Fields)
destination_h address Destination IP address (see Source and Destination Fields)
destination_p port Destination port (see Source and Destination Fields)
enip_command_code string Ethernet/IP command code
enip_command string Ethernet/IP command name
length count Length of ENIP data following header
session_handle string Session identifier
enip_status string Ethernet/IP status code
sender_context string Sender context
options string Options flags

CIP Header Log (cip.log)

Overview

This log captures CIP header information for every CIP packet and logs it to cip.log.

Fields Captured

Field Type Description
ts time Timestamp
uid string Unique ID for this connection
id conn_id Default Zeek connection info (IP addresses, ports)
is_orig bool True if the packet is sent from the originator
source_h address Source IP address (see Source and Destination Fields)
source_p port Source port (see Source and Destination Fields)
destination_h address Destination IP address (see Source and Destination Fields)
destination_p port Destination port (see Source and Destination Fields)
cip_sequence_count count CIP sequence number
direction string Request or response
cip_service_code string CIP service code
cip_service string CIP service name
cip_status_code string CIP status code
cip_status string CIP status name
cip_extended_status_code string CIP extended status code
cip_extended_status string CIP extended status name
class_id string CIP request path - class ID
class_name string CIP request path - class name
instance_id string CIP request path - instance ID
attribute_id string CIP request path - attribute ID

CIP I/O Log (cip_io.log)

Overview

This log captures CIP I/O (input-output) data for every CIP IO packet and logs it to cip_io.log.

Fields Captured

Field Type Description
ts time Timestamp
uid string Unique ID for this connection
id conn_id Default Zeek connection info (IP addresses, ports)
is_orig bool True if the packet is sent from the originator
source_h address Source IP address (see Source and Destination Fields)
source_p port Source port (see Source and Destination Fields)
destination_h address Destination IP address (see Source and Destination Fields)
destination_p port Destination port (see Source and Destination Fields)
connection_id string Connection identifier
sequence_number count Sequence number within connection
data_length count Length of data in io_data field
io_data string CIP IO data (in hex)

CIP Identity Log (cip_identity.log)

Overview

This log captures important variables for CIP_Identity objects and logs them to cip_identity.log.

Fields Captured

Field Type Description
ts time Timestamp
uid string Unique ID for this connection
id conn_id Default Zeek connection info (IP addresses, ports)
is_orig bool True if the packet is sent from the originator
source_h address Source IP address (see Source and Destination Fields)
source_p port Source Port (see Source and Destination Fields)
destination_h address Destination IP address (see Source and Destination Fields)
destination_p port Destination Port (see Source and Destination Fields)
encapsulation_version count Encapsulation protocol version supported
socket_address addr Socket address IP address
socket_port count Socket address port number
vendor_id count Vendor ID
vendor_name string Name of vendor
device_type_id count Device type ID
device_type_name string Name of device type
product_code count Product code assigned to device
revision string Device revision (major.minor)
device_status string Current status of device
serial_number string Serial number of device
product_name string Human readable description of device
device_state string Current state of the device

Source and Destination Fields

Overview

Zeek's typical behavior is to focus on and log packets from the originator and not log packets from the responder. However, most ICS protocols contain useful information in the responses, so the ICSNPP parsers log both originator and responses packets. Zeek's default behavior, defined in its id struct, is to never switch these originator/responder roles which leads to inconsistencies and inaccuracies when looking at ICS traffic that logs responses.

The default Zeek id struct contains the following logged fields:

  • id.orig_h (Original Originator/Source Host)
  • id.orig_p (Original Originator/Source Port)
  • id.resp_h (Original Responder/Destination Host)
  • id.resp_p (Original Responder/Destination Port)

Additionally, the is_orig field is a boolean field that is set to T (True) when the id_orig fields are the true originators/source and F (False) when the id_resp fields are the true originators/source.

To not break existing platforms that utilize the default id struct and is_orig field functionality, the ICSNPP team has added four new fields to each log file instead of changing Zeek's default behavior. These four new fields provide the accurate information regarding source and destination IP addresses and ports:

  • source_h (True Originator/Source Host)
  • source_p (True Originator/Source Port)
  • destination_h (True Responder/Destination Host)
  • destination_p (True Responder/Destination Port)

The pseudocode below shows the relationship between the id struct, is_orig field, and the new source and destination fields.

if is_orig == True
    source_h == id.orig_h
    source_p == id.orig_p
    destination_h == id.resp_h
    destination_p == id.resp_p
if is_orig == False
    source_h == id.resp_h
    source_p == id.resp_p
    destination_h == id.orig_h
    destination_p == id.orig_p

Example

The table below shows an example of these fields in the log files. The first log in the table represents a Modbus request from 192.168.1.10 -> 192.168.1.200 and the second log represents a Modbus reply from 192.168.1.200 -> 192.168.1.10. As shown in the table below, the id structure lists both packets as having the same originator and responder, but the source and destination fields reflect the true source and destination of these packets.

id.orig_h id.orig_p id.resp_h id.resp_p is_orig source_h source_p destination_h destination_p
192.168.1.10 47785 192.168.1.200 502 T 192.168.1.10 47785 192.168.1.200 502
192.168.1.10 47785 192.168.1.200 502 F 192.168.1.200 502 192.168.1.10 47785

Coverage

See Logging Capabilities for detailed information of the parser coverage.

Note

Ethernet/IP and CIP have a lot of vendor and product specific messages that are not included in this parser. All coverage details in this section include information and statistics based on the basic/default Ethernet/IP and CIP protocols.

General/Header Logging

The general log files for Ethernet/IP (enip.log) and CIP (cip.log) contain basic header information for all basic/default (~100%) Ethernet/IP and CIP messages.

Detailed Logging

Detailed logging for 1 Ethernet/IP command is logged in the CIP identity log file (cip_identity.log). The other 9 Ethernet/IP commands do not contain detailed logging, therefore 10% (1/10) of the Ethernet/IP commands contain detailed logging.

There is currently no (0%) detailed logging for regular CIP services outside of the CIP general/header log file (cip.log).

ICSNPP Packages

All ICSNPP Packages:

Full ICS Protocol Parsers:

  • BACnet
    • Full Zeek protocol parser for BACnet (Building Control and Automation)
  • BSAP
    • Full Zeek protocol parser for BSAP (Bristol Standard Asynchronous Protocol) over IP
    • Full Zeek protocol parser for BSAP Serial comm converted using serial tap device
  • Ethercat
    • Full Zeek protocol parser for Ethercat
  • Ethernet/IP and CIP
    • Full Zeek protocol parser for Ethernet/IP and CIP
  • GE SRTP
    • Full Zeek protocol parser for GE SRTP
  • Genisys
    • Full Zeek protocol parser for Genisys
  • OPCUA-Binary
    • Full Zeek protocol parser for OPC UA (OPC Unified Architecture) - Binary
  • S7Comm
    • Full Zeek protocol parser for S7comm, S7comm-plus, and COTP
  • Synchrophasor
    • Full Zeek protocol parser for Synchrophasor Data Transfer for Power Systems (C37.118)
  • Profinet IO CM
    • Full Zeek protocol parser for Profinet I/O Context Manager

Updates to Zeek ICS Protocol Parsers:

  • DNP3
    • DNP3 Zeek script extending logging capabilities of Zeek's default DNP3 protocol parser
  • Modbus
    • Modbus Zeek script extending logging capabilities of Zeek's default Modbus protocol parser

License

Copyright 2023 Battelle Energy Alliance, LLC. Released under the terms of the 3-Clause BSD License (see LICENSE.txt).

icsnpp-enip's People

Contributors

kkvarfordt avatar kleinspider avatar mmguero avatar nagilum2007 avatar piercema avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

icsnpp-enip's Issues

CIP identity logs not being generated in some cases based on communication direction

🐛 Summary

I've got a PCAP that reproduces this I can share internally with the team. From what I can tell this has to do with the orig_h/resp_h orig_p/resp_p swapping stuff that happened somewhat recently. In this PCAP, orig_p is 61696 and resp_p is 44818, but source_p is 44818 and destination_p is 61696 in the CIP log. if I add 61696 to dpd.sig, I get the identity file generated.

essentially: this is the bug: although cip.log is created and is the same with the dpd.sig as-is, cip_identity.log is not created unless I add this PCAP's orig_p to the dst-port field.

To reproduce

I can provide the PCAP internally to the developers.

Expected behavior

cip_identity.log should be generated regardless of the direction.

Increase Asset Logging Capabilities Through the CIP Get-Attribute-All Event

Feature Request

Give the parser the ability to detect assets through the CIP Get-Attribute-All and ListIdentity event
in addition to the existing ENIP ListIdentity event.

Feature Context

The parser is currently implemented such that the cip-identity.log file is created exclusively from the
ENIP event ListIdentity. While this packet contains all necessary information to create an asset log, it
is infrequent and does not contain robust information. The much more common CIP Get-Attribute-All
event contains much of the same information and would also be able to create a more
robust asset log for a given device. An interesting feature to the parser would be the ability to create
asset logs in cip-identity.log from both the ENIP and CIP equivalent events.

Feature Value Add

Using the much more common CIP event would make it possible for users to map the assets on their
network in the event that the ENIP log wasn’t seen. This gives parser users overall a better
understanding of their network environments and the assets communicating within them.

Additionally, the CIP event has more robust information about status and value tracking. The CIP
identity request is fairly common within ‘steady state’ polling between the vendor software and PLCs.
With a larger of volume of events each yielding a lot of data, users may be able to more accurately
capture mode changes during operation and other behaviors such as firmware updates in progress.

Lastly, the addition of this feature would allow for previously undiscovered devices to be logged. For
example, device modules that are connected via the backplane, which are themselves not directly
network accessible, cannot be logged via the ENIP ListIdentity event. With the CIP Get-Attribute-All
event however, these devices can be logged and tied back to their controller devices via the “path
data” field in the packet.

Implementation Notes

The CIP class 0x01 is associated with the device identity by the CIP specification and would be the
primary focus for implementing this identity tracking through Get-Attribute-All requests.

One potential problem is, since these behaviors are fairly common within standard EWS to PLC
communication, there may be many more events created by the addition of this feature. Also, depending
on where these logs are placed, this may lead to conflicting or duplicate logs across logging files.

icsnpp-enip package build fails in Zeek v6.0.0 release candidate

Installing "https://github.com/cisagov/icsnpp-enip"
error: incomplete installation, the follow packages failed to be installed:
  https://github.com/cisagov/icsnpp-enip (main)
Failed installing "https://github.com/cisagov/icsnpp-enip": package build_command failed, see log in /opt/zeek-rc/var/lib/zkg/logs/icsnpp-enip-build.log

Originator and Responder IPs/ports Not Flipping for Logged Responder Packets

Summary

Zeek's default of dropping responder packets is being overridden by the parser. Therefore, responder
packets are being logged; however, the originator and responder roles aren't switching and packets are
ending up mislabeled as all originator -> responder despite them actually being responder -> originator
(for example, in an ACK to a command).

Motivation and Context

As currently implemented, the Zeek output is inconsistent and inaccurate when compared to
communication over the wire as seen in Wireshark. Over the wire we would expect to see things like the
originator sends a packet to the responder and the responder responds to the originator, and so on. In
the current implementation however, we see responder packets being mislabeled as having the same
source/destination IP/port as the originator packets.

This behavior is because the parser has been implemented in-line with the standard Zeek method of
parser implementation. That is, Zeek focuses on and logs, typically, only the packets going from the
originator to responder for any given established communication. While this works for many other
protocols, given the importance of response packets to this protocol, this parser has chosen to include
responder packets in the logging schema. Since Zeek isn’t expecting to see any responder packets, it is
not automatically configured to account for a new source/destination IP/port unless otherwise
specified.

Fixes and Implementation Notes

The proposed fixes will allow this parser to use built-in Zeek functionality in a wrapper function to take
advantage of the automatically assigned originator and responder roles. It will use the is_originator
variable and the FlipRoles() function included in Zeek to flip the roles when the packet is coming from
the responder. This will retain all other information in the sent packet except for the source IP/port and
destination IP/port, which will be flipped.

Fixes:

  1. Make a series of standardized changes to enip-protocol.pac that will give the is_originator
    functionality to all of the functions that can make use of it so that roles can be flipped.
  2. Create a function that takes the variable is_orig as a parameter in enip-analyzer.pac and flips
    the roles of originators and responders when the variable is False.
  3. Make a series of standardized changes to enip-analyzer.pac so that functions can take
    advantage of the flip roles function described above by using the is_orig variable.

Code Comparison Between Current Implementation and Proposed Fixes

enip-protocol.pac
Comparing files enip-protocol.pac and ENIP-PROTOCOL-WITH-FIXES.PAC

***** enip-protocol.pac
58: sequenced_address_length: uint16; # Always 8
59: sequenced_address_item: Sequenced_Address_Item;
60: connected_data_type: uint16; # Always 0x00B1
***** ENIP-PROTOCOL-WITH-FIXES.PAC
58: sequenced_address_length: uint16; # Always 8
59: sequenced_address_item: Sequenced_Address_Item(is_orig);
60: connected_data_type: uint16; # Always 0x00B1
*****

***** enip-protocol.pac
192: ## ------------------------------------------------------------------------------------------------
193: type Register_Session = record {
194: protocol_version : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
192: ## ------------------------------------------------------------------------------------------------
193: type Register_Session(is_orig: bool) = record {
194: protocol_version : uint16;
*****

***** enip-protocol.pac
196: } &let {
197: deliver: bool = $context.flow.process_register_session(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
196: } &let {
197: is_originator: bool = is_orig;
198: deliver: bool = $context.flow.process_register_session(this);
*****

***** enip-protocol.pac
277: NULL_ADDRESS -> null_address: empty;
278: CIP_IDENTITY -> cip_identity_item: CIP_Identity_Item;
279: CIP_SECURITY -> cip_security_item: CIP_Security_Item;
280: ENIP_CAPABILITY -> enip_capability: ENIP_Capability_Item;
281: CONNECTED_ADDRESS -> connected_address: Connected_Address_Item;
282: CONNECTED_TRANSPORT_DATA -> connected_transport_data:
Connected_Data_Item(is_orig, item_length);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
278: NULL_ADDRESS -> null_address: empty;
279: CIP_IDENTITY -> cip_identity_item: CIP_Identity_Item(is_orig);
280: CIP_SECURITY -> cip_security_item: CIP_Security_Item(is_orig);
281: ENIP_CAPABILITY -> enip_capability: ENIP_Capability_Item(is_orig);
282: CONNECTED_ADDRESS -> connected_address:
Connected_Address_Item(is_orig);
283: CONNECTED_TRANSPORT_DATA -> connected_transport_data:
Connected_Data_Item(is_orig, item_length);
*****

***** enip-protocol.pac
283: UNCONNECTED_MESSAGE_DATA -> unconnected_message_data:
Unconnected_Data_Item(is_orig, item_length);
284: LIST_SERVICES_RESPONSE -> list_services_response: Service_Item;
285: SOCK_ADDR_DATA_ORIG_TO_TARGET -> sock_addr_data_orig_to_target:
Socket_Address_Info_Item;
286: SOCK_ADDR_DATA_TARGET_TO_ORIG -> sock_addr_data_target_to_orig:
Socket_Address_Info_Item;
287: SEQUENCED_ADDRESS_ITEM -> sequenced_address_item:
Sequenced_Address_Item;
288: UNCONNECTED_MESSAGE_DTLS -> unconnected_message_dtls:
Unconnected_Message_DTLS(is_orig, item_length);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
284: UNCONNECTED_MESSAGE_DATA -> unconnected_message_data:
Unconnected_Data_Item(is_orig, item_length);
285: LIST_SERVICES_RESPONSE -> list_services_response: Service_Item(is_orig);
286: SOCK_ADDR_DATA_ORIG_TO_TARGET -> sock_addr_data_orig_to_target:
Socket_Address_Info_Item(is_orig);
287: SOCK_ADDR_DATA_TARGET_TO_ORIG -> sock_addr_data_target_to_orig:
Socket_Address_Info_Item(is_orig);
288: SEQUENCED_ADDRESS_ITEM -> sequenced_address_item:
Sequenced_Address_Item(is_orig);
289: UNCONNECTED_MESSAGE_DTLS -> unconnected_message_dtls:
Unconnected_Message_DTLS(is_orig, item_length);
*****

***** enip-protocol.pac
299: ## - Socket Address Info: struct -> See Socket_Address_Info_Item
300: ## - Vendor ID: uint16 -> Device manufacturer's Vendor ID
301: ## - Device Type: uint16 -> Device type of product
***** ENIP-PROTOCOL-WITH-FIXES.PAC
300: ## - Socket Address Info: struct -> See Socket_Address_Info_Item
301: ## - Vendor ID: uint16 -> Device manufacturer''s Vendor ID
302: ## - Device Type: uint16 -> Device type of product
*****

***** enip-protocol.pac
313: ## ------------------------------------------------------------------------------------------------
314: type CIP_Identity_Item = record {
315: encapsulation_version : uint16;
316: socket_address : Socket_Address_Info_Item;
317: vendor_id : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
314: ## ------------------------------------------------------------------------------------------------
315: type CIP_Identity_Item (is_orig: bool) = record {
316: encapsulation_version : uint16;
317: socket_address : Socket_Address_Info_Item(is_orig);
318: vendor_id : uint16;
*****

***** enip-protocol.pac
327: } &let {
328: deliver: bool = $context.flow.process_cip_identity_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
328: } &let {
329: is_originator : bool = is_orig;
330: deliver: bool = $context.flow.process_cip_identity_item(this);
*****

***** enip-protocol.pac
349: ## ------------------------------------------------------------------------------------------------
350: type CIP_Security_Item = record {
351: security_profile : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
351: ## ------------------------------------------------------------------------------------------------
352: type CIP_Security_Item(is_orig: bool) = record {
353: security_profile : uint16;
*****

***** enip-protocol.pac
355: } &let {
356: deliver: bool = $context.flow.process_cip_security_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
357: } &let {
358: is_originator: bool = is_orig;
359: deliver: bool = $context.flow.process_cip_security_item(this);
*****

***** enip-protocol.pac
367: ## ------------------------------------------------------------------------------------------------
368: type ENIP_Capability_Item = record {
369: enip_profile : uint32;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
370: ## ------------------------------------------------------------------------------------------------
371: type ENIP_Capability_Item(is_orig: bool) = record {
372: enip_profile : uint32;
*****

***** enip-protocol.pac
370: } &let {
371: deliver: bool = $context.flow.process_enip_capability_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
373: } &let {
374: is_originator: bool = is_orig;
375: deliver: bool = $context.flow.process_enip_capability_item(this);
*****

***** enip-protocol.pac
385: ## ------------------------------------------------------------------------------------------------
386: type Socket_Address_Info_Item = record {
387: sin_family : int16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
389: ## ------------------------------------------------------------------------------------------------
390: type Socket_Address_Info_Item(is_orig: bool) = record {
391: sin_family : int16;
*****

***** enip-protocol.pac
391: } &let {
392: deliver: bool = $context.flow.process_socket_address_info(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
395: } &let {
396: is_originator: bool = is_orig;
397: deliver: bool = $context.flow.process_socket_address_info(this);
*****

***** enip-protocol.pac
404: ## ------------------------------------------------------------------------------------------------
405: type Service_Item = record {
406: protocol_version : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
409: ## ------------------------------------------------------------------------------------------------
410: type Service_Item(is_orig: bool) = record {
411: protocol_version : uint16;
*****

***** enip-protocol.pac
409: } &let {
410: deliver: bool = $context.flow.process_service_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
414: } &let {
415: is_originator: bool = is_orig;
416: deliver: bool = $context.flow.process_service_item(this);
*****

***** enip-protocol.pac
421: ## ------------------------------------------------------------------------------------------------
422: type Connected_Address_Item = record {
423: connection_identifier : uint32;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
427: ## ------------------------------------------------------------------------------------------------
428: type Connected_Address_Item(is_orig: bool) = record {
429: connection_identifier : uint32;
*****

***** enip-protocol.pac
424: } &let {
425: deliver: bool = $context.flow.process_connected_address_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
430: } &let {
431: is_originator: bool = is_orig;
432: deliver: bool = $context.flow.process_connected_address_item(this);
*****

***** enip-protocol.pac
437: ## ------------------------------------------------------------------------------------------------
438: type Sequenced_Address_Item = record {
439: connection_identifier : uint32;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
444: ## ------------------------------------------------------------------------------------------------
445: type Sequenced_Address_Item(is_orig: bool) = record {
446: connection_identifier : uint32;
*****

***** enip-protocol.pac
441: } &let {
442: deliver: bool = $context.flow.process_sequenced_address_item(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
448: } &let {
449: is_originator: bool = is_orig;
450: deliver: bool = $context.flow.process_sequenced_address_item(this);
*****

***** enip-protocol.pac
488: } &let {
489: deliver: bool = $context.flow.process_unconnected_message_dtls(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
496: } &let {
497: is_originator: bool = is_orig;
498: deliver: bool = $context.flow.process_unconnected_message_dtls(this);
*****

***** enip-protocol.pac
521: GET_ATTRIBUTES_ALL -> get_attributes_all_request: empty;
522: GET_ATTRIBUTES_ALL_RESPONSE -> get_attributes_all_response:
Get_Attributes_All_Response;
523: GET_ATTRIBUTE_LIST -> get_attribute_list: Get_Attribute_List_Request;
524: GET_ATTRIBUTE_LIST_RESPONSE -> get_attribute_list_response:
Get_Attribute_List_Response;
525: SET_ATTRIBUTE_LIST -> set_attribute_list: Set_Attribute_List_Request;
526: SET_ATTRIBUTE_LIST_RESPONSE -> set_attribute_list_response:
Set_Attribute_List_Response;
527: MULTIPLE_SERVICE -> multiple_service_request:
Multiple_Service_Packet_Request(is_orig, cip_sequen
***** ENIP-PROTOCOL-WITH-FIXES.PAC
530: GET_ATTRIBUTES_ALL -> get_attributes_all_request: empty;
531: GET_ATTRIBUTES_ALL_RESPONSE -> get_attributes_all_response:
Get_Attributes_All_Response(is_orig);
532: GET_ATTRIBUTE_LIST -> get_attribute_list:
Get_Attribute_List_Request(is_orig);
533: GET_ATTRIBUTE_LIST_RESPONSE -> get_attribute_list_response:
Get_Attribute_List_Response(is_orig);
534: SET_ATTRIBUTE_LIST -> set_attribute_list: Set_Attribute_List_Request(is_orig);
535: SET_ATTRIBUTE_LIST_RESPONSE -> set_attribute_list_response:
Set_Attribute_List_Response(is_orig);
536: MULTIPLE_SERVICE -> multiple_service_request:
Multiple_Service_Packet_Request(is_orig, cip_sequen
*****

***** enip-protocol.pac
530: nce_count, response_packet.status, response_packet.status_extended);
531: GET_ATTRIBUTE_SINGLE_RESPONSE -> get_attribute_single_response:
Get_Attribute_Single_Response;
532: SET_ATTRIBUTE_SINGLE -> set_attribute_single_request:
Set_Attribute_Single_Request;
533: default -> other: bytestring &restofdata;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
539: nce_count, response_packet.status, response_packet.status_extended);
540: GET_ATTRIBUTE_SINGLE_RESPONSE -> get_attribute_single_response:
Get_Attribute_Single_Response(is_orig);
541: SET_ATTRIBUTE_SINGLE -> set_attribute_single_request:
Set_Attribute_Single_Request(is_orig);
542: default -> other: bytestring &restofdata;
*****

***** enip-protocol.pac
582: ## ------------------------------------------------------------------------------------------------
583: type Get_Attributes_All_Response = record {
584: attribute_data : bytestring &restofdata;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
591: ## ------------------------------------------------------------------------------------------------
592: type Get_Attributes_All_Response(is_orig: bool) = record {
593: attribute_data : bytestring &restofdata;
*****

***** enip-protocol.pac
585: } &let {
586: deliver: bool = $context.flow.process_get_attribute_all_response(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
594: } &let {
595: is_originator: bool = is_orig;
596: deliver: bool = $context.flow.process_get_attribute_all_response(this);
*****

***** enip-protocol.pac
598: ## ------------------------------------------------------------------------------------------------
599: type Set_Attributes_All_Request = record {
600: attribute_data : bytestring &restofdata;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
608: ## ------------------------------------------------------------------------------------------------
609: type Set_Attributes_All_Request(is_orig: bool) = record {
610: attribute_data : bytestring &restofdata;
*****

***** enip-protocol.pac
601: } &let {
602: deliver: bool = $context.flow.process_set_attribute_all_request(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
611: } &let {
612: is_originator: bool = is_orig;
613: deliver: bool = $context.flow.process_set_attribute_all_request(this);
*****

***** enip-protocol.pac
615: ## ------------------------------------------------------------------------------------------------
616: type Get_Attribute_List_Request = record {
617: attribute_count : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
626: ## ------------------------------------------------------------------------------------------------
627: type Get_Attribute_List_Request(is_orig: bool) = record {
628: attribute_count : uint16;
*****

***** enip-protocol.pac
619: } &let {
620: deliver: bool = $context.flow.process_get_attribute_list_request(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
630: } &let {
631: is_originator: bool = is_orig;
632: deliver: bool = $context.flow.process_get_attribute_list_request(this);
*****

***** enip-protocol.pac
634: ## ------------------------------------------------------------------------------------------------
635: type Get_Attribute_List_Response = record {
636: attribute_count : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
646: ## ------------------------------------------------------------------------------------------------
647: type Get_Attribute_List_Response(is_orig: bool) = record {
648: attribute_count : uint16;
*****

***** enip-protocol.pac
638: } &let {
639: deliver: bool = $context.flow.process_get_attribute_list_response(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
650: } &let {
651: is_originator: bool = is_orig;
652: deliver: bool = $context.flow.process_get_attribute_list_response(this);
*****

***** enip-protocol.pac
653: ## ------------------------------------------------------------------------------------------------
654: type Set_Attribute_List_Request = record {
655: attribute_count : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
666: ## ------------------------------------------------------------------------------------------------
667: type Set_Attribute_List_Request(is_orig: bool) = record {
668: attribute_count : uint16;
*****

***** enip-protocol.pac
657: } &let {
658: deliver: bool = $context.flow.process_set_attribute_list_request(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
670: } &let {
671: is_originator: bool = is_orig;
672: deliver: bool = $context.flow.process_set_attribute_list_request(this);
*****

***** enip-protocol.pac
672: ## ------------------------------------------------------------------------------------------------
673: type Set_Attribute_List_Response = record {
674: attribute_count : uint16;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
686: ## ------------------------------------------------------------------------------------------------
687: type Set_Attribute_List_Response(is_orig: bool) = record {
688: attribute_count : uint16;
*****

***** enip-protocol.pac
676: } &let {
677: deliver: bool = $context.flow.process_set_attribute_list_response(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
690: } &let {
691: is_originator: bool = is_orig;
692: deliver: bool = $context.flow.process_set_attribute_list_response(this);
*****

***** enip-protocol.pac
731: ## ------------------------------------------------------------------------------------------------
732: type Get_Attribute_Single_Response = record {
733: attribute_data : bytestring &restofdata;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
746: ## ------------------------------------------------------------------------------------------------
747: type Get_Attribute_Single_Response(is_orig: bool) = record {
748: attribute_data : bytestring &restofdata;
*****

***** enip-protocol.pac
734: } &let {
735: deliver: bool = $context.flow.process_get_attribute_single_response(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
749: } &let {
750: is_originator: bool = is_orig;
751: deliver: bool = $context.flow.process_get_attribute_single_response(this);
*****

***** enip-protocol.pac
748: ## ------------------------------------------------------------------------------------------------
749: type Set_Attribute_Single_Request = record {
750: attribute_id : uint8;
***** ENIP-PROTOCOL-WITH-FIXES.PAC
764: ## ------------------------------------------------------------------------------------------------
765: type Set_Attribute_Single_Request(is_orig: bool) = record {
766: attribute_id : uint8;
*****

***** enip-protocol.pac
752: } &let {
753: deliver: bool = $context.flow.process_set_attribute_single_request(this);
***** ENIP-PROTOCOL-WITH-FIXES.PAC
768: } &let {
769: is_originator: bool = is_orig;
770: deliver: bool = $context.flow.process_set_attribute_single_request(this);
*****
enip-analyzer.pac
Comparing files enip-analyzer.pac and ENIP-ANALYZER-WITH-FIXES.PAC

***** enip-analyzer.pac
149:
150:
#####################################################################################
##########
***** ENIP-ANALYZER-WITH-FIXES.PAC
149:
150:
151: function flip_roles_when_responder(is_orig: bool): void
152: %{
153: if(!is_orig)
154: {
155: connection()->zeek_analyzer()->Conn()->FlipRoles();
156: }
157: %}
158:
159:
#####################################################################################
##########
*****

***** enip-analyzer.pac
156: {
157: zeek::BifEvent::enqueue_enip_header(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
165: {
166: flip_roles_when_responder(${enip_header.is_originator});
167: zeek::BifEvent::enqueue_enip_header(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
165: ${enip_header.options});
166: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
175: ${enip_header.options});
176: flip_roles_when_responder(${enip_header.is_originator});
177: }
*****

***** enip-analyzer.pac
194:
***** ENIP-ANALYZER-WITH-FIXES.PAC
194: flip_roles_when_responder(${cip_header.is_originator});
*****

***** enip-analyzer.pac
206: request_path.attribute_id);
207: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
218: request_path.attribute_id);
219: flip_roles_when_responder(${cip_header.is_originator});
220: }
*****

***** enip-analyzer.pac
215: %{
216: if ( ::cip_io )
217: {
218: zeek::BifEvent::enqueue_cip_io(connection()->zeek_analyzer(),
219: connection()->zeek_analyzer()->Conn(),
220: ${cip_io_item.is_originator},
221: ${cip_io_item.sequenced_address_item.connection_identifier},
222: ${cip_io_item.sequenced_address_item.encap_sequence_number},
223: ${cip_io_item.connected_data_length},
224: to_stringval(${cip_io_item.connected_data_item}));
225: }
226: return true;
***** ENIP-ANALYZER-WITH-FIXES.PAC
228: %{
229: if ( ::cip_io )
230: {
231: flip_roles_when_responder(${cip_io_item.is_originator});
232: zeek::BifEvent::enqueue_cip_io(connection()->zeek_analyzer(),
233: connection()->zeek_analyzer()->Conn(),
234: ${cip_io_item.is_originator},
235: ${cip_io_item.sequenced_address_item.connection_identifier},
236: ${cip_io_item.sequenced_address_item.encap_sequence_number},
237: ${cip_io_item.connected_data_length},
238: to_stringval(${cip_io_item.connected_data_item}));
239: flip_roles_when_responder(${cip_io_item.is_originator});
240: }
241: return true;
*****

***** enip-analyzer.pac
235: {
236: zeek::BifEvent::enqueue_cip_identity(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
250: {
251: flip_roles_when_responder(${identity_item.is_originator});
252: zeek::BifEvent::enqueue_cip_identity(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
249: ${identity_item.state});
250: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
265: ${identity_item.state});
266: flip_roles_when_responder(${identity_item.is_originator});
267: }
*****

***** enip-analyzer.pac
275: {
276: zeek::BifEvent::enqueue_cip_security(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
294: {
295: flip_roles_when_responder(${security_item.is_originator});
296: zeek::BifEvent::enqueue_cip_security(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
281: ${security_item.iana_port_state});
282: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
301: ${security_item.iana_port_state});
302: flip_roles_when_responder(${security_item.is_originator});
303: }
*****

***** enip-analyzer.pac
292: {
293: zeek::BifEvent::enqueue_enip_capability(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
313: {
314: flip_roles_when_responder(${enip_item.is_originator});
315: zeek::BifEvent::enqueue_enip_capability(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
295: ${enip_item.enip_profile});
296: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
317: ${enip_item.enip_profile});
318: flip_roles_when_responder(${enip_item.is_originator});
319: }
*****

***** enip-analyzer.pac
306: {
307: zeek::BifEvent::enqueue_enip_service(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
329: {
330: flip_roles_when_responder(${service_item.is_originator});
331: zeek::BifEvent::enqueue_enip_service(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
311: to_stringval(${service_item.service_name}));
312: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
335: to_stringval(${service_item.service_name}));
336: flip_roles_when_responder(${service_item.is_originator});
337: }
*****

***** enip-analyzer.pac
322: {
323: zeek::BifEvent::enqueue_connected_address(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
347: {
348: flip_roles_when_responder(${address_item.is_originator});
349: zeek::BifEvent::enqueue_connected_address(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
325: ${address_item.connection_identifier});
326: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
351: ${address_item.connection_identifier});
352: flip_roles_when_responder(${address_item.is_originator});
353: }
*****

***** enip-analyzer.pac
336: {
337: zeek::BifEvent::enqueue_sequenced_address(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
363: {
364: flip_roles_when_responder(${address_item.is_originator});
365: zeek::BifEvent::enqueue_sequenced_address(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
340: ${address_item.encap_sequence_number});
341: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
368: ${address_item.encap_sequence_number});
369: flip_roles_when_responder(${address_item.is_originator});
370: }
*****

***** enip-analyzer.pac
351: {
352: zeek::BifEvent::enqueue_unconnected_message_dtls(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
380: {
381: flip_roles_when_responder(${message.is_originator});
382: zeek::BifEvent::enqueue_unconnected_message_dtls(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
356: ${message.status});
357: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
386: ${message.status});
387: flip_roles_when_responder(${message.is_originator});
388: }
*****

***** enip-analyzer.pac
367: {
368: zeek::BifEvent::enqueue_socket_address_info(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
398: {
399: flip_roles_when_responder(${item.is_originator});
400: zeek::BifEvent::enqueue_socket_address_info(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
371: ${item.sin_port});
372: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
403: ${item.sin_port});
404: flip_roles_when_responder(${item.is_originator});
405: }
*****

***** enip-analyzer.pac
382: {
383: zeek::BifEvent::enqueue_get_attribute_all_response(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
415: {
416: flip_roles_when_responder(${data.is_originator});
417: zeek::BifEvent::enqueue_get_attribute_all_response(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
385: to_stringval(${data.attribute_data}));
386: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
419: to_stringval(${data.attribute_data}));
420: flip_roles_when_responder(${data.is_originator});
421: }
*****

***** enip-analyzer.pac
396: {
397: zeek::BifEvent::enqueue_set_attribute_all_request(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
431: {
432: flip_roles_when_responder(${data.is_originator});
433: zeek::BifEvent::enqueue_set_attribute_all_request(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
399: to_stringval(${data.attribute_data}));
400: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
435: to_stringval(${data.attribute_data}));
436: flip_roles_when_responder(${data.is_originator});
437: }
*****

***** enip-analyzer.pac
410: {
411: string attribute_ids = zeek::util::fmt("%d",${data.attribute_list[0]});
***** ENIP-ANALYZER-WITH-FIXES.PAC
447: {
448: flip_roles_when_responder(${data.is_originator});
449: string attribute_ids = zeek::util::fmt("%d",${data.attribute_list[0]});
*****

***** enip-analyzer.pac
419: zeek::make_intrusive<zeek::StringVal>(attribute_ids));
420: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
457: zeek::make_intrusive<zeek::StringVal>(attribute_ids));
458: flip_roles_when_responder(${data.is_originator});
459: }
*****

***** enip-analyzer.pac
430: {
431: zeek::BifEvent::enqueue_get_attribute_list_response(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
469: {
470: flip_roles_when_responder(${data.is_originator});
471: zeek::BifEvent::enqueue_get_attribute_list_response(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
434: to_stringval(${data.attribute_data}));
435: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
474: to_stringval(${data.attribute_data}));
475: flip_roles_when_responder(${data.is_originator});
476: }
*****

***** enip-analyzer.pac
445: {
446: zeek::BifEvent::enqueue_set_attribute_list_request(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
486: {
487: flip_roles_when_responder(${data.is_originator});
488: zeek::BifEvent::enqueue_set_attribute_list_request(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
449: to_stringval(${data.attribute_data}));
450: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
491: to_stringval(${data.attribute_data}));
492: flip_roles_when_responder(${data.is_originator});
493: }
*****

***** enip-analyzer.pac
460: {
461: zeek::BifEvent::enqueue_set_attribute_list_response(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
503: {
504: flip_roles_when_responder(${data.is_originator});
505: zeek::BifEvent::enqueue_set_attribute_list_response(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
464: to_stringval(${data.attribute_data}));
465: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
508: to_stringval(${data.attribute_data}));
509: flip_roles_when_responder(${data.is_originator});
510: }
*****

***** enip-analyzer.pac
475: {
476: uint16 service_packet_location;
***** ENIP-ANALYZER-WITH-FIXES.PAC
520: {
521: flip_roles_when_responder(${data.is_originator});
522: uint16 service_packet_location;
*****

***** enip-analyzer.pac
512: }
513: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
558: }
559: flip_roles_when_responder(${data.is_originator});
560: }
*****

***** enip-analyzer.pac
524: {
525: CIP_Request_Path request_path;
***** ENIP-ANALYZER-WITH-FIXES.PAC
571: {
572: flip_roles_when_responder(${data.is_originator});
573: CIP_Request_Path request_path;
*****

***** enip-analyzer.pac
560: }
561: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
608: }
609: flip_roles_when_responder(${data.is_originator});
610: }
*****

***** enip-analyzer.pac
571: {
572: zeek::BifEvent::enqueue_get_attribute_single_response(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
620: {
621: flip_roles_when_responder(${data.is_originator});
622: zeek::BifEvent::enqueue_get_attribute_single_response(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
574: to_stringval(${data.attribute_data}));
575: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
624: to_stringval(${data.attribute_data}));
625: flip_roles_when_responder(${data.is_originator});
626: }
*****

***** enip-analyzer.pac
585: {
586: zeek::BifEvent::enqueue_set_attribute_single_request(connection()->zeek_analyzer(),
***** ENIP-ANALYZER-WITH-FIXES.PAC
636: {
637: flip_roles_when_responder(${data.is_originator});
638: zeek::BifEvent::enqueue_set_attribute_single_request(connection()->zeek_analyzer(),
*****

***** enip-analyzer.pac
589: to_stringval(${data.attribute_data}));
590: }
***** ENIP-ANALYZER-WITH-FIXES.PAC
641: to_stringval(${data.attribute_data}));
642: flip_roles_when_responder(${data.is_originator});
643: }
*****

Non-enip traffic reported as enip if on standard 44818 port

🐛 Summary

The parser is returning false positives on any traffic that has a destination port == 44818. So far my co-worker and I have tested this out with https traffic, dns traffic, and netcat udp traffic. I've attached some zip files of pcaps and zeek logs that were generated from those pcaps that show the falsely identified traffic. We noticed false positives with https and netcat traffic to the standard enip port, but not with dns. These pcaps were edited with scapy after capture to modify port values. The checksums were recalculated after editing the pcaps with scapy.

We've also noticed many false positives where the source port is 44818, but the traffic is just normal https or dns traffic to 443/53. However, we have not been able to reproduce this locally.

dns_47581_to_44818.zip
https_443_to_44818.zip
https_59951_to_44818.zip
netcat_49865_to_44818.zip

To reproduce

Steps to reproduce the behavior:

  1. Capture https traffic or udp traffic with netcat where the dst port is 44818
  2. Use scapy to edit pcap if neccesary to make source port = 44818

Expected behavior

We expect the https/udp traffic to not be registered as ENIP traffic when the dst port is 44818.

Any helpful log output or screenshots

Paste the results here:

Add any screenshots of the problem here.

Alive Connections Being Dropped

Summary

Connections are being prematurely severed based on occasionally seen TCP communication gaps
(typically [TCP ACKed unseen segment] or [TCP Retransmission] packets). While the devices autonegotiate
a fix to these gaps and continue with their communication, Zeek has measures in place to alert
parsers of these gaps and leave it up to them on how to handle it. The current implementation is more
in-line with how other Zeek parsers handle these types of packet sequences, however it is resulting in a
high volume of packet loss in this instance.

Motivation and Context

ENIP communications may occasionally send [TCP ACKed unseen segment] or [TCP Retransmission]
packets, which results in a TCP gap being formed. While this doesn’t impact the communication
between devices, Zeek sees this as a failed connection and sets the had_gap variable to true so that
parsers can know that this communication is faulty. The variable will be reset for that communication on
a per parser and per connection basis, meaning that it is up to the parsers to handle the gap and tell
Zeek what to do next.

Lines 37 and 38, if (had_gap) return;, in ENIP.cc handle the error. These lines result in all future events,
for a given TCP connection, being skipped if the parser detected a gap. However, since this flag is being set
during the normal communications of healthy and ongoing connections, a high volume of unrecoverable
packet loss occurs.

As currently implemented, the large amount of packet loss in the final parser output leads to
inconsistent and inaccurate results when compared with communication over the wire. If this condition
is realized, Wireshark will show significantly more traffic than Zeek, as Zeek has stopped logging all
traffic from that connection. This fix would allow for reestablished/retransmitted connections to
continue to be logged. Therefore, anyone who is viewing traffic will have an accurate understanding of
the relationship between assets and the true volume of traffic being sent.

Potential Fixes and Implementation Notes

Comment out or remove the if (had_gap) return; line in ENIP.cc.

Code Comparison Between Current Implementation and Proposed Fixes

Comparing files ENIP.cc and ENIP-WITH-FIXES.CC
***** ENIP.cc
36: assert(TCP());
37: if(had_gap)
38: return;
39:
***** ENIP-WITH-FIXES.CC
36: assert(TCP());
37: //if(had_gap)
38: // return;
39:
*****

Extended Status Codes

💡 Summary

What is the work, as a high-level summary?
Addition of extended status codes that provide more detailed information on the base cip_status field.

Motivation and context

Why does this work belong in this project?

This would be useful because...

When searching for connection errors in CIP devices more detail from these extended status codes may provide extra useful information for the analyst when performing a hunt or analysis of an attack. This may help create the possibility of detecting specific types of attacks that have specific changes noted by the extended status.

Implementation notes

Please provide details for implementation, such as:

  • an example for how this would be used
  • what this would look like
  • how this would act
  • any related work, including links to related issues

Addition of cip_extended_status or similar field in the parser. It would be helpful to include both the extended status code and extended status code value for easier parsing/searching.

Lines 1224 and 1342 contain the beginning of the wireshark dissector's extended status codes https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-cip.c

Acceptance criteria

How do we know when this work is done?

  • Extended Status Codes are correctly being parsed
  • cip_extended_status or similar field is available
  • optionally extended status code values field is available for easier parsing/searching

Error while running icsnpp-enip

🐛 Summary

What's wrong? Please be specific.

Downloaded iscnpp-enip and when I ran it got below error.

Error

zeek -Cr icsnpp-enip/tests/traces/enip_cip_example.pcap icsnpp/enip

error in ./icsnpp/enip/main.zeek, line 162: unknown identifier Analyzer::ANALYZER_ENIP_UDP, at or near "Analyzer::ANALYZER_ENIP_UDP"

Steps followed

git clone https://github.com/cisagov/icsnpp-enip.git
zeek -Cr icsnpp-enip/tests/traces/enip_cip_example.pcap icsnpp/enip

Zeek version using: 6.1.0

Zeek Version-Specific ProtocolViolation() and AnalyzerViolation() Conditional

Summary

Zeek has deprecated the ProtocolViolation() function that is used to throw exceptions in the
corresponding pac files for parsers for Zeek version >= 4.2. This change was accompanied by the
removal of ProtocolViolation() from all default Zeek parsers and AnalyzerViolation() taking its place.
AnalyzerViolation() is not backwards compatible, therefore to allow this parser to be used with versions
before and after 4.2, this parser has removed the violation handler altogether. This proposed fix
implements a C++ preprocessor conditional that choses the correct function based on the Zeek version
so the violation functionality can be restored.

Motivation and Context

ProtocolViolation() and AnalyzerViolation() are useful functions to be used when packets are unable to
be delivered via the DeliverStream() function due to errors in the Zeek-received data. Zeek, instead of
exhibiting unexpected behavior, handles the data errors by throwing a standard exception for that
connection.

Since Zeek has officially deprecated ProtocolViolation(), parsers that contain that function will no longer
compile past Zeek 4.2. All prepackaged Zeek parsers have changed their violation function. To maintain
useability between versions < 4.2 and >= 4.2, this parser decided to remove the violation function
altogether in commit 0c610cd. While this achieved the goal of having the parser compile with
multiple Zeek versions, it may have unintended consequences if the parser is faced with unexpected data.

Fixes and Implementation Notes

The analyzer could use a C++ preprocessor conditional to choose which violation function to use based
on the Zeek version it is compiled with. It could do this by checking the current Zeek version against the
value 40200, which corresponds to Zeek version 4.02.00.

Code Comparison Between Current Implementation and Proposed Fixes

Comparing files ENIP.cc and ENIP-WITH-FIXES.CC

***** ENIP.cc
45: {
46: }
***** ENIP-WITH-FIXES.CC
45: {
46: #if ZEEK_VERSION_NUMBER < 40200
47: ProtocolViolation(util::fmt("Binpac exception: %s", e.c_msg()));
48:
49: #else
50: AnalyzerViolation(util::fmt("Binpac exception: %s", e.c_msg()));
51:
52: #endif
53: }
*****

***** ENIP.cc
81: {
82: }
***** ENIP-WITH-FIXES.CC
88: {
89: #if ZEEK_VERSION_NUMBER < 40200
90: ProtocolViolation(util::fmt("Binpac exception: %s", e.c_msg()));
91:
92: #else
93: AnalyzerViolation(util::fmt("Binpac exception: %s", e.c_msg()));
94:
95: #endif
96: }
*****

Collisions with DNS

I have seen collisions with enip on a network I can monitor, but can't share data from. We had a similar issue with bacnet and DNS and what I did was only search for bacnet via the dpd sig on ports >= 1024. Maybe something similar should be done here? At least port 53 should be ignored with these signatures.

payload /^[\x00\x04\x63\x64\x65\x66\x6f\x70\x72\x73]\x00[\x00-\xff]{6}[\x00\x01\x02\x03\x64\x65\x69\6a]\x00{3}[\x00-\xff][\x00-\xff]{4}/

Capture wrong direction of communication

🐛 Summary

Capture wrong direction of communication

To reproduce

Version of zeek LTS: 5.0.9
Version of enip plugin: 2.12.0

Just run the pcap file in attachment

Expected behavior

The broadcast messages should be from 192.168.1.105 not from 255.255.255.255

Any helpful log output or screenshots

conn.log
enip.log

See conn.log and enip.log files in attachment
enip_Issue.zip

ListIdentity Endianness Change

Summary

When parsing a ListIdentity event (type CIP_Identity_Item starting on line 314 in enip-protocol.pac),
the parser will insert both the sin_addr and sin_port into the log in the opposite endianness as the
packet (this is logged by type Socket_Address_Info_Item on line 386 in enip-protocol.pac). This
results in incorrect values being logged. For example, the IP address 123.456.7.8 will be inserted as
8.7.456.123 and the port will be converted to decimal incorrectly as the raw bytes were read in
backwards.

Fixes and Implementation Notes

A potential fix to this issue could be changing the byteorder on enip-protocol.pac line 393 in type Socket_Address_Info_item from littleendian to bigendian.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.