Modbus: Add support for Encapsulation Interface Transport (FC=2B) requests and responses

This commit is contained in:
Tim Wojtulewicz 2023-07-20 13:26:13 -07:00
parent f14be0de29
commit 406a406813
14 changed files with 143 additions and 21 deletions

View file

@ -315,3 +315,25 @@ event modbus_diagnostics_request%(c: connection, headers: ModbusHeaders, subfunc
##
## data: The data passed in the diagnostics response.
event modbus_diagnostics_response%(c: connection, headers: ModbusHeaders, subfunction: count, data: string%);
## Generated for a Modbus Encapsulated Interface Transport request.
##
## c: The connection.
##
## headers: The headers for the modbus function.
##
## mei_type: The MEI type for the request.
##
## data: The MEI type specific data passed in the request.
event modbus_encap_interface_transport_request%(c: connection, headers: ModbusHeaders, mei_type: count, data: string%);
## Generated for a Modbus Encapsulated Interface Transport response.
##
## c: The connection.
##
## headers: The headers for the modbus function.
##
## mei_type: The MEI type for the response.
##
## data: The MEI type specific data passed in the response.
event modbus_encap_interface_transport_response%(c: connection, headers: ModbusHeaders, mei_type: count, data: string%);

View file

@ -761,4 +761,31 @@ refine flow ModbusTCP_Flow += {
return true;
%}
# REQUEST FC=2B
function deliver_EncapInterfaceTransportRequest(header: ModbusTCP_TransportHeader, message: EncapInterfaceTransportRequest): bool
%{
if ( ::modbus_encap_interface_transport_request )
{
zeek::BifEvent::enqueue_modbus_encap_interface_transport_request(
connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(),
HeaderToVal(header), ${message.mei_type}, to_stringval(${message.data}));
}
return true;
%}
# RESPONSE FC=2B
function deliver_EncapInterfaceTransportResponse(header: ModbusTCP_TransportHeader, message: EncapInterfaceTransportResponse): bool
%{
if ( ::modbus_encap_interface_transport_response )
{
zeek::BifEvent::enqueue_modbus_encap_interface_transport_response(
connection()->zeek_analyzer(), connection()->zeek_analyzer()->Conn(),
HeaderToVal(header), ${message.mei_type}, to_stringval(${message.data}));
}
return true;
%}
};

View file

@ -56,6 +56,7 @@ enum function_codes {
MASK_WRITE_REGISTER_EXCEPTION = 0x96,
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x97,
READ_FIFO_QUEUE_EXCEPTION = 0x98,
ENCAP_INTERFACE_TRANSPORT_EXCEPTION = 0xAB,
};
enum diagnostic_subfunctions {
@ -116,7 +117,7 @@ type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterRequest(header);
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersRequest(header);
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueRequest(header);
#ENCAP_INTERFACE_TRANSPORT
ENCAP_INTERFACE_TRANSPORT -> encapInterfaceException: EncapInterfaceTransportRequest(header);
# All the rest
default -> unknown: bytestring &restofdata;
@ -143,23 +144,25 @@ type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterResponse(header);
READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersResponse(header);
READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueResponse(header);
ENCAP_INTERFACE_TRANSPORT -> encapInterfaceException: EncapInterfaceTransportResponse(header);
# Exceptions
READ_HOLDING_REGISTERS_EXCEPTION -> readHoldingRegistersException: Exception(header);
WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultRegistersException: Exception(header);
READ_COILS_EXCEPTION -> readCoilsException: Exception(header);
READ_DISCRETE_INPUTS_EXCEPTION -> readDiscreteInputsException: Exception(header);
READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header);
WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header);
WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header);
READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header);
DIAGNOSTICS_EXCEPTION -> diagnosticsException: Exception(header);
WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header);
READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header);
WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header);
MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header);
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header);
READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header);
READ_HOLDING_REGISTERS_EXCEPTION -> readHoldingRegistersException: Exception(header);
WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultRegistersException: Exception(header);
READ_COILS_EXCEPTION -> readCoilsException: Exception(header);
READ_DISCRETE_INPUTS_EXCEPTION -> readDiscreteInputsException: Exception(header);
READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header);
WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header);
WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header);
READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header);
DIAGNOSTICS_EXCEPTION -> diagnosticsException: Exception(header);
WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header);
READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header);
WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header);
MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header);
READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header);
READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header);
ENCAP_INTERFACE_TRANSPORT_EXCEPTION -> encapInterfaceTransportException: Exception(header);
# All the rest
default -> unknown: bytestring &restofdata;
@ -430,3 +433,19 @@ type ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader) = record {
} &let {
deliver: bool = $context.flow.deliver_ReadFIFOQueueResponse(header, this);
} &byteorder=bigendian;
# REQUEST FC=2B
type EncapInterfaceTransportRequest(header: ModbusTCP_TransportHeader) = record {
mei_type: uint8;
data: bytestring &restofdata;
} &let {
deliver: bool = $context.flow.deliver_EncapInterfaceTransportRequest(header, this);
} &byteorder=bigendian;
# RESPONSE FC=2B
type EncapInterfaceTransportResponse(header: ModbusTCP_TransportHeader) = record {
mei_type: uint8;
data: bytestring &restofdata;
} &let {
deliver: bool = $context.flow.deliver_EncapInterfaceTransportResponse(header, this);
} &byteorder=bigendian;

View file

@ -1,2 +1,2 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
5 of 30 events triggered by trace
5 of 32 events triggered by trace

View file

@ -1,2 +1,2 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
5 of 30 events triggered by trace
5 of 32 events triggered by trace

View file

@ -0,0 +1,12 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string]
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 192.168.2.42 54297 192.168.88.100 502 tcp modbus 0.022532 11 18 OTH T T 0 Dd 1 51 1 58 -
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 192.168.2.42 54298 192.168.88.100 502 tcp modbus 3.019519 11 9 OTH T T 0 Dd 1 51 1 49 -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -1,2 +1,2 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
20 of 30 events triggered by trace
22 of 32 events triggered by trace

View file

@ -6,6 +6,8 @@
2 modbus_diagnostics_response, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 1, \x00\x00
2 modbus_diagnostics_response, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 10, \x00\x00
1 modbus_diagnostics_response, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=8], 0, \x00\x00
2 modbus_encap_interface_transport_request, [orig_h=10.0.0.57, orig_p=2579/tcp, resp_h=10.0.0.8, resp_p=502/tcp], [tid=1, pid=0, uid=10, function_code=43], 14, \x03\x01\x00
1 modbus_encap_interface_transport_request, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=43], 0, \x00\x00\x00
4 modbus_exception, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=136], 11
8 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=129], 2
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=129], 3
@ -20,6 +22,7 @@
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=150], 3
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=151], 3
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=152], 3
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=171], 1
4 modbus_message, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=136], F
2 modbus_message, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=17], F
2 modbus_message, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=17], T
@ -79643,3 +79646,11 @@
1 modbus_write_single_register_response, [orig_h=10.1.1.234, orig_p=51411/tcp, resp_h=10.10.5.85, resp_p=502/tcp], [tid=9776, pid=0, uid=255, function_code=6], 102, 1
1 modbus_write_single_register_response, [orig_h=10.1.1.234, orig_p=51411/tcp, resp_h=10.10.5.85, resp_p=502/tcp], [tid=9777, pid=0, uid=255, function_code=6], 500, 80
1 modbus_write_single_register_response, [orig_h=10.1.1.234, orig_p=51411/tcp, resp_h=10.10.5.85, resp_p=502/tcp], [tid=9778, pid=0, uid=255, function_code=6], 502, 60
1 modbus_encap_interface_transport_request, [orig_h=192.168.2.42, orig_p=54297/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=0, function_code=43], 14, \x01\x00
1 modbus_encap_interface_transport_request, [orig_h=192.168.2.42, orig_p=54298/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=255, function_code=43], 14, \x01\x00
1 modbus_encap_interface_transport_response, [orig_h=192.168.2.42, orig_p=54297/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=0, function_code=43], 14, \x01\x00M\xb7\x00\x00\x00\x00\x00
1 modbus_exception, [orig_h=192.168.2.42, orig_p=54298/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=255, function_code=171], 4
1 modbus_message, [orig_h=192.168.2.42, orig_p=54297/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=0, function_code=43], F
1 modbus_message, [orig_h=192.168.2.42, orig_p=54297/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=0, function_code=43], T
1 modbus_message, [orig_h=192.168.2.42, orig_p=54298/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=255, function_code=171], F
1 modbus_message, [orig_h=192.168.2.42, orig_p=54298/tcp, resp_h=192.168.88.100, resp_p=502/tcp], [tid=0, pid=0, uid=255, function_code=43], T

View file

@ -9,3 +9,13 @@
#types time string addr port addr port string string bool string string
XXXXXXXXXX.XXXXXX - - - - - modbus_diag_unknown_request_subfunction 0 F zeek -
#close XXXX-XX-XX-XX-XX-XX
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path weird
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source
#types time string addr port addr port string string bool string string
XXXXXXXXXX.XXXXXX - - - - - modbus_diag_unknown_request_subfunction 0 F zeek -
#close XXXX-XX-XX-XX-XX-XX

View file

@ -123,6 +123,7 @@ XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-42 RESP -
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 ENCAP_INTERFACE_TRANSPORT REQ -
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-171 RESP -
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-171 RESP ILLEGAL_FUNCTION
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-44 REQ -
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-172 RESP -
XXXXXXXXXX.XXXXXX C37jN32gN3y3AZzyf6 192.168.66.235 2582 166.161.16.230 502 0 1 unknown-45 REQ -

View file

@ -3,3 +3,7 @@ These are the trace files that are used by the Zeek test suite.
Note to maintainers: please take care when modifying/removing files from here.
We install these traces with the Zeek distribution and external packages might
depend on them for tests.
Trace Index/Sources:
- modbus/modbus-eit.trace: Sourced from https://www.netresec.com/?page=PCAP4SICS, credit to https://cs3sthlm.se/. The packets in this trace were pulled from the 4SICS-GeekLounge-151021.pcap file.

Binary file not shown.

View file

@ -1,12 +1,18 @@
#
# @TEST-EXEC: zeek -b -r $TRACES/modbus/modbus.trace %INPUT | sort | uniq -c | sed 's/^ *//g' >output
# @TEST-EXEC: mv conn.log conn-first.log
# @TEST-EXEC: cat weird.log >> weird-combined.log
# @TEST-EXEC: zeek -b -r $TRACES/modbus/modbus-eit.pcap %INPUT | sort | uniq -c | sed 's/^ *//g' >>output
# @TEST-EXEC: mv conn.log conn-second.log
# @TEST-EXEC: cat weird.log >> weird-combined.log
# @TEST-EXEC: btest-diff output
# @TEST-EXEC: cat output | awk '{print $2}' | grep "^modbus_" | sort | uniq | wc -l >covered
# @TEST-EXEC: cat ${DIST}/src/analyzer/protocol/modbus/events.bif | grep "^event modbus_" | wc -l >total
# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage
# @TEST-EXEC: btest-diff coverage
# @TEST-EXEC: btest-diff conn.log
# @TEST-EXEC: btest-diff weird.log
# @TEST-EXEC: btest-diff conn-first.log
# @TEST-EXEC: btest-diff conn-second.log
# @TEST-EXEC: btest-diff weird-combined.log
@load base/protocols/modbus
@load base/protocols/conn
@ -163,3 +169,13 @@ event modbus_diagnostics_response(c: connection, headers: ModbusHeaders, subfunc
{
print "modbus_diagnostics_response", c$id, headers, subfunction, data;
}
event modbus_encap_interface_transport_request(c: connection, headers: ModbusHeaders, mei_type: count, data: string)
{
print "modbus_encap_interface_transport_request", c$id, headers, mei_type, data;
}
event modbus_encap_interface_transport_response(c: connection, headers: ModbusHeaders, mei_type: count, data: string)
{
print "modbus_encap_interface_transport_response", c$id, headers, mei_type, data;
}