From: Oleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) Date: Tue, 11 Oct 2022 17:28:58 +0000 (+0000) Subject: Pull request #3566: s7commplus: adding wizard support for s7commplus X-Git-Tag: 3.1.45.0~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=830dd3d2325dd0c26c92638640a765e09538398c;p=thirdparty%2Fsnort3.git Pull request #3566: s7commplus: adding wizard support for s7commplus Merge in SNORT/snort3 from ~JRITTLE/snort3:s7comm_inspector_curse to master Squashed commit of the following: commit 03fe0712ecc431aff21c1ce2ff95ed416dcc3733 Author: Jared Rittle Date: Wed Aug 10 00:04:27 2022 -0400 s7commplus: adding wizard support for s7commplus --- diff --git a/lua/snort.lua b/lua/snort.lua index f2262bf7a..e2357a150 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -121,7 +121,6 @@ binder = -- port bindings required for protocols without wizard support { when = { proto = 'udp', ports = '53', role='server' }, use = { type = 'dns' } }, { when = { proto = 'tcp', ports = '53', role='server' }, use = { type = 'dns' } }, - { when = { proto = 'tcp', ports = '102', role = 'server' }, use = { type = 's7commplus' } }, { when = { proto = 'tcp', ports = '111', role='server' }, use = { type = 'rpc_decode' } }, { when = { proto = 'tcp', ports = '502', role='server' }, use = { type = 'modbus' } }, { when = { proto = 'tcp', ports = '2123 2152 3386', role='server' }, use = { type = 'gtp_inspect' } }, diff --git a/lua/snort_defaults.lua b/lua/snort_defaults.lua index 1153987aa..a1c70f003 100644 --- a/lua/snort_defaults.lua +++ b/lua/snort_defaults.lua @@ -415,7 +415,7 @@ default_wizard = to_server = telnet_commands, to_client = telnet_commands }, }, - curses = {'dce_udp', 'dce_tcp', 'dce_smb', 'mms', 'sslv2'} + curses = {'dce_udp', 'dce_tcp', 'dce_smb', 'mms', 's7commplus', 'sslv2'} } --------------------------------------------------------------------------- diff --git a/src/service_inspectors/wizard/CMakeLists.txt b/src/service_inspectors/wizard/CMakeLists.txt index 63eb3c132..33ab8c803 100644 --- a/src/service_inspectors/wizard/CMakeLists.txt +++ b/src/service_inspectors/wizard/CMakeLists.txt @@ -5,6 +5,7 @@ set(FILE_LIST magic.cc magic.h mms_curse.h + s7commplus_curse.h hexes.cc spells.cc wizard.cc diff --git a/src/service_inspectors/wizard/curses.cc b/src/service_inspectors/wizard/curses.cc index be258c5e8..18471a8eb 100644 --- a/src/service_inspectors/wizard/curses.cc +++ b/src/service_inspectors/wizard/curses.cc @@ -16,7 +16,7 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- // curses.cc author Maya Dagon -// mms curse author Jared Rittle +// mms_curse and s7commplus_curse author Jared Rittle #ifdef HAVE_CONFIG_H #include "config.h" @@ -494,6 +494,245 @@ static bool mms_curse(const uint8_t* data, unsigned len, CurseTracker* tracker) } +static bool s7commplus_curse(const uint8_t* data, unsigned len, CurseTracker* tracker) +{ + // peg the tracker to s7commplus + CurseTracker::S7COMMPLUS& s7commplus = tracker->s7commplus; + + // if the state is set to S7COMMPLUS_STATE__SEARCH it means we most likely + // have a split pipelined message coming through and will need to + // reset the state + if ( s7commplus.state == S7COMMPLUS_STATE__SEARCH ) + { + s7commplus.state = s7commplus.last_state; + } + + uint32_t idx = 0; + while ( idx < len ) + { + switch ( s7commplus.state ) + { + case S7COMMPLUS_STATE__TPKT_VER: + { + s7commplus.state = S7COMMPLUS_STATE__TPKT_RES; + break; + } + + case S7COMMPLUS_STATE__TPKT_RES: + { + s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN1; + break; + } + + case S7COMMPLUS_STATE__TPKT_LEN1: + { + s7commplus.state = S7COMMPLUS_STATE__TPKT_LEN2; + break; + } + + case S7COMMPLUS_STATE__TPKT_LEN2: + { + s7commplus.state = S7COMMPLUS_STATE__COTP_LEN; + break; + } + + case S7COMMPLUS_STATE__COTP_LEN: + { + s7commplus.state = S7COMMPLUS_STATE__COTP_PDU; + break; + } + + case S7COMMPLUS_STATE__COTP_PDU: + { + // 7 6 5 4 3 2 1 0 + // --------------- + // . . . . x x x x Destination Reference + // x x x x . . . . PDU Type + const uint32_t S7COMMPLUS_COTP_PDU_DT_DATA = 0x0F; + + if ( data[idx] >> 0x04 != S7COMMPLUS_COTP_PDU_DT_DATA ) + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + break; + } + + s7commplus.state = S7COMMPLUS_STATE__COTP_TPDU_NUM; + break; + } + + case S7COMMPLUS_STATE__COTP_TPDU_NUM: + { + s7commplus.state = S7COMMPLUS_STATE__PROTO_ID; + break; + } + + case S7COMMPLUS_STATE__PROTO_ID: + { + // there are two possible protocol identifiers - 0x32 and 0x72 + // 0x32 indicates the original s7comm protocol + // * the original protocol is not supported within the inspector + // so just catching and considering it a no match for now + // 0x72 indicates the s7commplus protocol + // * this is the protocol on which the existing inspector focuses + if ( data[idx] == S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS ) + { + s7commplus.state = S7COMMPLUS_STATE__PDU_TYPE; + } + else + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + } + break; + } + + case S7COMMPLUS_STATE__PDU_TYPE: + { + switch ( data[idx] ) + { + case S7COMMPLUS_PDU_TYPE__CONNECT: // fallthrough intentional + case S7COMMPLUS_PDU_TYPE__DATA: // fallthrough intentional + case S7COMMPLUS_PDU_TYPE__DATA2: // fallthrough intentional + case S7COMMPLUS_PDU_TYPE__KEEPALIVE: + { + s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_1; + break; + } + + default: + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + break; + } + } + + break; + } + + case S7COMMPLUS_STATE__DATALENGTH_1: + { + s7commplus.state = S7COMMPLUS_STATE__DATALENGTH_2; + break; + } + + case S7COMMPLUS_STATE__DATALENGTH_2: + { + s7commplus.state = S7COMMPLUS_STATE__OPCODE; + break; + } + + case S7COMMPLUS_STATE__OPCODE: + { + switch ( data[idx] ) + { + case S7COMMPLUS_OPCODE__REQ: // fallthrough intentional + case S7COMMPLUS_OPCODE__RES: // fallthrough intentional + case S7COMMPLUS_OPCODE__NOTIFICATION: // fallthrough intentional + case S7COMMPLUS_OPCODE__RES2: + { + s7commplus.state = S7COMMPLUS_STATE__RES_1; + break; + } + + default: + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + break; + } + } + + break; + } + + case S7COMMPLUS_STATE__RES_1: + { + s7commplus.state = S7COMMPLUS_STATE__RES_2; + break; + } + + case S7COMMPLUS_STATE__RES_2: + { + s7commplus.state = S7COMMPLUS_STATE__FUNCTION_1; + break; + } + + case S7COMMPLUS_STATE__FUNCTION_1: + { + // make sure the function code is zeroed out before building + s7commplus.func = 0; + + // get the high byte of the function code + s7commplus.func = data[idx] << 0x08; + + // move on to the low byte + s7commplus.state = S7COMMPLUS_STATE__FUNCTION_2; + break; + } + + case S7COMMPLUS_STATE__FUNCTION_2: + { + // get the low byte of the function code + s7commplus.func |= data[idx]; + + switch ( s7commplus.func ) + { + case S7COMMPLUS_FUNCTION__EXPLORE: // fallthrough intentional + case S7COMMPLUS_FUNCTION__CREATEOBJECT: // fallthrough intentional + case S7COMMPLUS_FUNCTION__DELETEOBJECT: // fallthrough intentional + case S7COMMPLUS_FUNCTION__SETVARIABLE: // fallthrough intentional + case S7COMMPLUS_FUNCTION__GETLINK: // fallthrough intentional + case S7COMMPLUS_FUNCTION__SETMULTIVAR: // fallthrough intentional + case S7COMMPLUS_FUNCTION__GETMULTIVAR: // fallthrough intentional + case S7COMMPLUS_FUNCTION__BEGINSEQUENCE: // fallthrough intentional + case S7COMMPLUS_FUNCTION__ENDSEQUENCE: // fallthrough intentional + case S7COMMPLUS_FUNCTION__INVOKE: // fallthrough intentional + case S7COMMPLUS_FUNCTION__GETVARSUBSTR: + { + s7commplus.state = S7COMMPLUS_STATE__FOUND; + break; + } + + default: + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + break; + } + } + + break; + } + + case S7COMMPLUS_STATE__FOUND: + { + s7commplus.state = S7COMMPLUS_STATE__TPKT_VER; + + return true; + } + + case S7COMMPLUS_STATE__NOT_FOUND: + { + s7commplus.state = S7COMMPLUS_STATE__TPKT_VER; + + return false; + } + + default: + { + s7commplus.state = S7COMMPLUS_STATE__NOT_FOUND; + assert(false); + break; + } + } + + idx++; + } + + s7commplus.last_state = s7commplus.state; + s7commplus.state = S7COMMPLUS_STATE__SEARCH; + + return false; +} + + namespace SSL_Const { static constexpr uint8_t hdr_len = 9; @@ -645,12 +884,13 @@ static bool ssl_v2_curse(const uint8_t* data, unsigned len, CurseTracker* tracke // map between service and curse details static vector curse_map { - // name service alg is_tcp - { "dce_udp", "dcerpc" , dce_udp_curse, false }, - { "dce_tcp", "dcerpc" , dce_tcp_curse, true }, - { "mms" , "mms" , mms_curse , true }, - { "dce_smb", "netbios-ssn", dce_smb_curse, true }, - { "sslv2" , "ssl" , ssl_v2_curse , true } + // name service alg is_tcp + { "dce_udp" , "dcerpc" , dce_udp_curse , false }, + { "dce_tcp" , "dcerpc" , dce_tcp_curse , true }, + { "mms" , "mms" , mms_curse , true }, + { "s7commplus", "s7commplus" , s7commplus_curse, true }, + { "dce_smb" , "netbios-ssn", dce_smb_curse , true }, + { "sslv2" , "ssl" , ssl_v2_curse , true } }; bool CurseBook::add_curse(const char* key) diff --git a/src/service_inspectors/wizard/curses.h b/src/service_inspectors/wizard/curses.h index c7815efe4..8aee2fbee 100644 --- a/src/service_inspectors/wizard/curses.h +++ b/src/service_inspectors/wizard/curses.h @@ -25,6 +25,7 @@ #include #include "mms_curse.h" +#include "s7commplus_curse.h" enum DCE_State { @@ -73,6 +74,13 @@ public: MMS_State last_state; } mms; + struct S7COMMPLUS + { + S7commplus_State state; + S7commplus_State last_state; + uint16_t func; + } s7commplus; + struct SSL { SSL_State state; @@ -87,6 +95,9 @@ public: dce.state = DCE_State::STATE_0; mms.state = MMS_State::MMS_STATE__TPKT_VER; mms.last_state = mms.state; + s7commplus.state = S7commplus_State::S7COMMPLUS_STATE__TPKT_VER; + s7commplus.last_state = s7commplus.state; + s7commplus.func = 0; ssl.state = SSL_State::BYTE_0_LEN_MSB; } }; diff --git a/src/service_inspectors/wizard/s7commplus_curse.h b/src/service_inspectors/wizard/s7commplus_curse.h new file mode 100644 index 000000000..bba01a957 --- /dev/null +++ b/src/service_inspectors/wizard/s7commplus_curse.h @@ -0,0 +1,93 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022-2022 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// s7commplus_curse.h author Jared Rittle + +#ifndef S7COMMPLUS_CURSE_H +#define S7COMMPLUS_CURSE_H + +// s7commplus_curse provides the ability to determine if the traffic being processed +// conforms to the S7CommPlus protocol used in select Siemens devices + +#include "curses.h" + +enum S7commplus_Protocol_Identifier +{ + S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMM = 0x32, + S7COMMPLUS_PROTOCOL_IDENTIFIER__S7COMMPLUS = 0x72, +}; + + +enum S7commplus_Pdu_Type +{ + S7COMMPLUS_PDU_TYPE__CONNECT = 0x01, + S7COMMPLUS_PDU_TYPE__DATA = 0x02, + S7COMMPLUS_PDU_TYPE__DATA2 = 0x03, + S7COMMPLUS_PDU_TYPE__KEEPALIVE = 0xFF, +}; + + +enum S7commplus_Opcode +{ + S7COMMPLUS_OPCODE__REQ = 0x31, + S7COMMPLUS_OPCODE__RES = 0x32, + S7COMMPLUS_OPCODE__NOTIFICATION = 0x33, + S7COMMPLUS_OPCODE__RES2 = 0x02, +}; + + +enum S7commplus_Function +{ + S7COMMPLUS_FUNCTION__EXPLORE = 0x04BB, + S7COMMPLUS_FUNCTION__CREATEOBJECT = 0x04CA, + S7COMMPLUS_FUNCTION__DELETEOBJECT = 0x04D4, + S7COMMPLUS_FUNCTION__SETVARIABLE = 0x04F2, + S7COMMPLUS_FUNCTION__GETLINK = 0x0524, + S7COMMPLUS_FUNCTION__SETMULTIVAR = 0x0542, + S7COMMPLUS_FUNCTION__GETMULTIVAR = 0x054C, + S7COMMPLUS_FUNCTION__BEGINSEQUENCE = 0x0556, + S7COMMPLUS_FUNCTION__ENDSEQUENCE = 0x0560, + S7COMMPLUS_FUNCTION__INVOKE = 0x056B, + S7COMMPLUS_FUNCTION__GETVARSUBSTR = 0x0586, +}; + + +enum S7commplus_State +{ + S7COMMPLUS_STATE__TPKT_VER = 0, + S7COMMPLUS_STATE__TPKT_RES, + S7COMMPLUS_STATE__TPKT_LEN1, + S7COMMPLUS_STATE__TPKT_LEN2, + S7COMMPLUS_STATE__COTP_LEN, + S7COMMPLUS_STATE__COTP_PDU, + S7COMMPLUS_STATE__COTP_TPDU_NUM, + S7COMMPLUS_STATE__PROTO_ID, + S7COMMPLUS_STATE__PDU_TYPE, + S7COMMPLUS_STATE__DATALENGTH_1, + S7COMMPLUS_STATE__DATALENGTH_2, + S7COMMPLUS_STATE__OPCODE, + S7COMMPLUS_STATE__RES_1, + S7COMMPLUS_STATE__RES_2, + S7COMMPLUS_STATE__FUNCTION_1, + S7COMMPLUS_STATE__FUNCTION_2, + S7COMMPLUS_STATE__FOUND, + S7COMMPLUS_STATE__SEARCH, + S7COMMPLUS_STATE__NOT_FOUND, +}; + +#endif + diff --git a/src/service_inspectors/wizard/wiz_module.cc b/src/service_inspectors/wizard/wiz_module.cc index bcc79f56d..fce518d96 100644 --- a/src/service_inspectors/wizard/wiz_module.cc +++ b/src/service_inspectors/wizard/wiz_module.cc @@ -102,7 +102,7 @@ static const Parameter s_params[] = { "spells", Parameter::PT_LIST, wizard_spells_params, nullptr, "criteria for text service identification" }, - { "curses", Parameter::PT_MULTI, "dce_smb | dce_udp | dce_tcp | mms | sslv2", nullptr, + { "curses", Parameter::PT_MULTI, "dce_smb | dce_udp | dce_tcp | mms | s7commplus | sslv2", nullptr, "enable service identification based on internal algorithm" }, { "max_search_depth", Parameter::PT_INT, "0:65535", "8192",