App-layer: Add Modbus protocol parser

Decode Modbus request and response messages, and extracts
MODBUS Application Protocol header and the code function.

In case of read/write function, extracts message contents
(read/write address, quantity, count, data to write).

Links request and response messages in a transaction according to
Transaction Identifier (transaction management based on DNS source code).

MODBUS Messaging on TCP/IP Implementation Guide V1.0b
(http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf)
MODBUS Application Protocol Specification V1.1b3
(http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf)

Based on DNS source code.

Signed-off-by: David DIALLO <diallo@et.esia.fr>
pull/1207/head
DIALLO David 11 years ago committed by Victor Julien
parent 0b28943487
commit 5a0409959f

@ -41,6 +41,7 @@ endif
@test -e "$(e_sysconfrulesdir)smtp-events.rules" || install -m 600 "$(top_srcdir)/rules/smtp-events.rules" "$(e_sysconfrulesdir)"
@test -e "$(e_sysconfrulesdir)http-events.rules" || install -m 600 "$(top_srcdir)/rules/http-events.rules" "$(e_sysconfrulesdir)"
@test -e "$(e_sysconfrulesdir)dns-events.rules" || install -m 600 "$(top_srcdir)/rules/dns-events.rules" "$(e_sysconfrulesdir)"
@test -e "$(e_sysconfrulesdir)modbus-events.rules" || install -m 600 "$(top_srcdir)/rules/modbus-events.rules" "$(e_sysconfrulesdir)"
@echo ""
@echo "You can now start suricata by running as root something like '$(bindir)/suricata -c $(e_sysconfdir)/suricata.yaml -i eth0'."
@echo ""

@ -5,4 +5,5 @@ smtp-events.rules \
http-events.rules \
dns-events.rules \
tls-events.rules \
modbus-events.rules \
files.rules

@ -0,0 +1,18 @@
# Modbus Protocol version field is incorrect (Modbus version = 0)
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Protocol version"; app-layer-event:modbus.invalid_protocol_id; sid:2250001; rev:1;)
# Response (answer) we didn't see a Request for. Could be packet loss.
alert modbus any any -> any any (msg:"SURICATA Modbus unsolicited response"; app-layer-event:modbus.unsolicited_response; sid:2250002; rev:1;)
# Malformed request or response. Malformed means length field is wrong
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Length"; app-layer-event:modbus.invalid_length; sid:2250003; rev:1;)
# Unit identifier field is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Unit Identifier"; app-layer-event:modbus.invalid_unit_identifier; sid:2250004; rev:1;)
# Modbus Function code is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Function code"; app-layer-event:modbus.invalid_function_code; sid:2250005; rev:1;)
# Modbus Request/Response value field is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus invalid Value"; app-layer-event:modbus.invalid_value; sid:2250006; rev:1;)
# Modbus Expception code is incorrect
alert modbus any any -> any any (msg:"SURICATA Modbus Exception code invalid"; flow:to_client; app-layer-event:modbus.invalid_exception_code; sid:2250007; rev:1;)
# Value field in Modbus Response does not match with Modbus Request
alert modbus any any -> any any (msg:"SURICATA Modbus Data mismatch"; flow:to_client; app-layer-event:modbus.value_mismatch; sid:2250008; rev:1;)
# Request Flood Detected
alert modbus any any -> any any (msg:"SURICATA Modbus Request flood detected"; flow:to_server; app-layer-event:modbus.flooded; sid:2250009; rev:1;)

@ -26,6 +26,7 @@ app-layer-htp.c app-layer-htp.h \
app-layer-htp-file.c app-layer-htp-file.h \
app-layer-htp-libhtp.c app-layer-htp-libhtp.h \
app-layer-htp-mem.c app-layer-htp-mem.h \
app-layer-modbus.c app-layer-modbus.h \
app-layer-parser.c app-layer-parser.h \
app-layer-protos.c app-layer-protos.h \
app-layer-smb2.c app-layer-smb2.h \

File diff suppressed because it is too large Load Diff

@ -0,0 +1,128 @@
/*
* Copyright (C) 2014 ANSSI
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* \author David DIALLO <diallo@et.esiea.fr>
*/
#ifndef __APP_LAYER_MODBUS_H__
#define __APP_LAYER_MODBUS_H__
#include "decode.h"
#include "queue.h"
/* Modbus Application Data Unit (ADU)
* and Protocol Data Unit (PDU) messages */
enum {
MODBUS_DECODER_EVENT_INVALID_PROTOCOL_ID,
MODBUS_DECODER_EVENT_UNSOLICITED_RESPONSE,
MODBUS_DECODER_EVENT_INVALID_LENGTH,
MODBUS_DECODER_EVENT_INVALID_UNIT_IDENTIFIER,
MODBUS_DECODER_EVENT_INVALID_FUNCTION_CODE,
MODBUS_DECODER_EVENT_INVALID_VALUE,
MODBUS_DECODER_EVENT_INVALID_EXCEPTION_CODE,
MODBUS_DECODER_EVENT_VALUE_MISMATCH,
MODBUS_DECODER_EVENT_FLOODED,
};
/* Modbus Function Code Categories. */
#define MODBUS_CAT_NONE 0x0
#define MODBUS_CAT_PUBLIC_ASSIGNED (1<<0)
#define MODBUS_CAT_PUBLIC_UNASSIGNED (1<<1)
#define MODBUS_CAT_USER_DEFINED (1<<2)
#define MODBUS_CAT_RESERVED (1<<3)
#define MODBUS_CAT_ALL 0xFF
/* Modbus Read/Write function and Access Types. */
#define MODBUS_TYP_NONE 0x0
#define MODBUS_TYP_ACCESS_MASK 0x03
#define MODBUS_TYP_READ (1<<0)
#define MODBUS_TYP_WRITE (1<<1)
#define MODBUS_TYP_ACCESS_FUNCTION_MASK 0x3C
#define MODBUS_TYP_BIT_ACCESS_MASK 0x0C
#define MODBUS_TYP_DISCRETES (1<<2)
#define MODBUS_TYP_COILS (1<<3)
#define MODBUS_TYP_WORD_ACCESS_MASK 0x30
#define MODBUS_TYP_INPUT (1<<4)
#define MODBUS_TYP_HOLDING (1<<5)
#define MODBUS_TYP_SINGLE (1<<6)
#define MODBUS_TYP_MULTIPLE (1<<7)
#define MODBUS_TYP_WRITE_SINGLE (MODBUS_TYP_WRITE | MODBUS_TYP_SINGLE)
#define MODBUS_TYP_WRITE_MULTIPLE (MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
#define MODBUS_TYP_READ_WRITE_MULTIPLE (MODBUS_TYP_READ | MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE)
/* Modbus Transaction Structure, request/response. */
typedef struct ModbusTransaction_ {
struct ModbusState_ *modbus;
uint64_t tx_num; /**< internal: id */
uint16_t transactionId;
uint16_t length;
uint8_t function;
uint8_t category;
uint8_t type;
union {
uint16_t subFunction;
uint8_t mei;
struct {
struct {
uint16_t address;
uint16_t quantity;
} read;
struct {
uint16_t address;
uint16_t quantity;
uint8_t count;
} write;
};
};
uint16_t *data; /**< to store data to write, bit is converted in 16bits. */
AppLayerDecoderEvents *decoder_events; /**< per tx events */
uint8_t replied; /**< bool indicating request is replied to. */
TAILQ_ENTRY(ModbusTransaction_) next;
} ModbusTransaction;
/* Modbus State Structure. */
typedef struct ModbusState_ {
TAILQ_HEAD(, ModbusTransaction_) tx_list; /**< transaction list */
ModbusTransaction *curr; /**< ptr to current tx */
uint64_t transaction_max;
uint32_t unreplied_cnt; /**< number of unreplied requests */
uint16_t events;
uint8_t givenup; /**< bool indicating flood. */
} ModbusState;
void RegisterModbusParsers(void);
void ModbusParserRegisterTests(void);
#endif /* __APP_LAYER_MODBUS_H__ */

@ -57,6 +57,7 @@
#include "app-layer-smtp.h"
#include "app-layer-dns-udp.h"
#include "app-layer-dns-tcp.h"
#include "app-layer-modbus.h"
#include "conf.h"
#include "util-spm.h"
@ -1033,6 +1034,7 @@ void AppLayerParserRegisterProtocolParsers(void)
RegisterSMTPParsers();
RegisterDNSUDPParsers();
RegisterDNSTCPParsers();
RegisterModbusParsers();
/** IMAP */
AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap");

@ -41,6 +41,7 @@ enum {
ALPROTO_IRC,
ALPROTO_DNS,
ALPROTO_MODBUS,
/* used by the probing parser when alproto detection fails
* permanently for that particular stream */

@ -293,6 +293,7 @@ const char * SCErrorToString(SCError err)
CASE_CODE (SC_WARN_XFF_INVALID_HEADER);
CASE_CODE (SC_ERR_THRESHOLD_SETUP);
CASE_CODE (SC_ERR_DNS_CONFIG);
CASE_CODE (SC_ERR_MODBUS_CONFIG);
CASE_CODE (SC_ERR_CONF_YAML_ERROR);
CASE_CODE (SC_ERR_CONF_NAME_TOO_LONG);
CASE_CODE (SC_ERR_APP_LAYER_PROTOCOL_DETECTION);

@ -269,6 +269,7 @@ typedef enum {
SC_WARN_XFF_INVALID_HEADER,
SC_ERR_THRESHOLD_SETUP,
SC_ERR_DNS_CONFIG,
SC_ERR_MODBUS_CONFIG,
SC_ERR_CONF_YAML_ERROR,
SC_ERR_CONF_NAME_TOO_LONG,
SC_ERR_APP_LAYER_PROTOCOL_DETECTION,

@ -1028,6 +1028,7 @@ rule-files:
- smtp-events.rules # available in suricata sources under rules dir
- dns-events.rules # available in suricata sources under rules dir
- tls-events.rules # available in suricata sources under rules dir
- modbus-events.rules # available in suricata sources under rules dir
classification-file: @e_sysconfdir@classification.config
reference-config-file: @e_sysconfdir@reference.config
@ -1081,6 +1082,8 @@ vars:
DNP3_PORTS: 20000
MODBUS_PORTS: 502
# Set the order of alerts bassed on actions
# The default order is pass, drop, reject, alert
# action-order:
@ -1186,6 +1189,19 @@ app-layer:
enabled: yes
detection-ports:
dp: 139
# Note: Modbus probe parser is minimalist due to the poor significant field
# Only Modbus message length (greater than Modbus header length)
# And Protocol ID (equal to 0) are checked in probing parser
# It is important to enable detection port and define Modbus port
# to avoid false positive
modbus:
# How many unreplied Modbus requests are considered a flood.
# If the limit is reached, app-layer-event:modbus.flooded; will match.
#request-flood: 500
enabled: yes
detection-ports:
dp: 502
# smb2 detection is disabled internally inside the engine.
#smb2:
# enabled: yes

Loading…
Cancel
Save