]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3566: s7commplus: adding wizard support for s7commplus
authorOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Tue, 11 Oct 2022 17:28:58 +0000 (17:28 +0000)
committerOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Tue, 11 Oct 2022 17:28:58 +0000 (17:28 +0000)
Merge in SNORT/snort3 from ~JRITTLE/snort3:s7comm_inspector_curse to master

Squashed commit of the following:

commit 03fe0712ecc431aff21c1ce2ff95ed416dcc3733
Author: Jared Rittle <jared@machine.local>
Date:   Wed Aug 10 00:04:27 2022 -0400

    s7commplus: adding wizard support for s7commplus

lua/snort.lua
lua/snort_defaults.lua
src/service_inspectors/wizard/CMakeLists.txt
src/service_inspectors/wizard/curses.cc
src/service_inspectors/wizard/curses.h
src/service_inspectors/wizard/s7commplus_curse.h [new file with mode: 0644]
src/service_inspectors/wizard/wiz_module.cc

index f2262bf7a7794062cf71c95676840a1fba08294b..e2357a15090bb70fb81720f30b4d5fb26b2eacf2 100644 (file)
@@ -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' } },
index 1153987aadee3d1c62cb432471a1d0e5faf2e4c5..a1c70f0034e58fbd2e0a6333b9c8812165270f7b 100644 (file)
@@ -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'}
 }
 
 ---------------------------------------------------------------------------
index 63eb3c132ab72ccfcae469e98bdc8e7d10d03e97..33ab8c8033de0139b2a9cf6c998575ea06557634 100644 (file)
@@ -5,6 +5,7 @@ set(FILE_LIST
     magic.cc
     magic.h
     mms_curse.h
+    s7commplus_curse.h
     hexes.cc
     spells.cc
     wizard.cc
index be258c5e8e16a01fa1aaa7db52a3c52af1997292..18471a8eb1049242c28140326493dc4717498f96 100644 (file)
@@ -16,7 +16,7 @@
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
 // curses.cc author Maya Dagon <mdagon@cisco.com>
-// mms curse author Jared Rittle <jared.rittle@cisco.com>
+// mms_curse and s7commplus_curse author Jared Rittle <jared.rittle@cisco.com>
 
 #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<CurseDetails> 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)
index c7815efe464b8d6b3ad64888d181385631d1c1b5..8aee2fbee95ad6621de80d1150c0ff1bc6679a93 100644 (file)
@@ -25,6 +25,7 @@
 #include <vector>
 
 #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 (file)
index 0000000..bba01a9
--- /dev/null
@@ -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 <jared.rittle@cisco.com>
+
+#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
+
index bcc79f56dbf4b8e5fb791935859c7bdbb4f8b00b..fce518d96239838870f2c9d983823fd145e5b86a 100644 (file)
@@ -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",