]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #275 in SNORT/snort3 from dceserver_snort2lua to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 17 Feb 2016 23:09:56 +0000 (18:09 -0500)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 17 Feb 2016 23:09:56 +0000 (18:09 -0500)
Squashed commit of the following:

commit 72b6073cc25eef2f4c783b0021bd8ddef902f4f9
Author: mdagon <mdagon@cisco.com>
Date:   Wed Feb 17 13:11:43 2016 -0500

    Code review comments: FIXIT , dev_notes format

commit fcdb6f451f7cd24b75c5cd66176b037904ceb43e
Author: mdagon <mdagon@cisco.com>
Date:   Wed Feb 17 09:48:54 2016 -0500

    dcerpc2_server snort2lua

src/service_inspectors/dce_rpc/dev_notes.txt
tools/snort2lua/preprocessor_states/CMakeLists.txt
tools/snort2lua/preprocessor_states/Makefile.am
tools/snort2lua/preprocessor_states/pps_dcerpc_server.cc [new file with mode: 0644]
tools/snort2lua/preprocessor_states/preprocessor_api.cc

index a5ccd955a9128ce786b59c811e797200e719853c..e641e8bd131eb622eb2c090d04de599e10ae6490 100644 (file)
@@ -1,16 +1,25 @@
-This directory contains all the files related to DCE-RPC protocol processing.
+This directory contains all the files related to DCE-RPC protocol
+processing.
 
-DCE is supported over TCP/IP, UDP, SMB and HTTP v1 Proxy and Server. 
+DCE is supported over TCP/IP, UDP, SMB and HTTP v1 Proxy and Server.
 The DCE-RPC preprocessor is split into four inspectors - one for each
-transport. This includes the configuration as well as the inspector modules.  
+transport. This includes the configuration as well as the inspector
+modules.
 
-The inspectors perform SMB desegmentation and DCE-RPC defragmentation to
-avoid rule evasion using these techniques. IPS rule options are supported
-for matching on interface, opnum and stub data.
+The inspectors perform SMB desegmentation and DCE-RPC defragmentation
+to avoid rule evasion using these techniques. IPS rule options are
+supported for matching on interface, opnum and stub data.
 
-For each inspector, configuration is separate for every server and the address/port
-mapping is handled by the binder. Since the infrastructure has no support for 
-"autodetect" all ports are treated the same currently and autodetection performed.
-The Snort 2x global configuration is now rolled into server configuration.
+The Snort 2x server configuration is now split between the inspectors.
+Options that are meaningful to all inspectors, such as policy, are
+copied into each inspector configuration.
+
+The address/port mapping is handled by the binder. Since the
+infrastructure has no support for "autodetect" all ports are treated
+the same currently and autodetection performed. There are currently no
+default autodetect ranges.
+
+The Snort 2x global configuration is now rolled into server
+configuration.
 
 
index d1c9fe1998098f027b864c1d28e65f9eb9be6742..c9762c0476dfd806cd81a2af52dd50f1f4fabe13 100644 (file)
@@ -2,6 +2,7 @@
 add_library(preprocessor_states
     pps_arpspoof.cc
     pps_bo.cc
+    pps_dcerpc_server.cc
     pps_dnp3.cc
     pps_frag3_engine.cc
     pps_frag3_global.cc
index 2d134834195adefc4fe6db3d4b21c59662a66eee..34358bd0b37d59ba62c2363638451f5fe949cdda 100644 (file)
@@ -4,6 +4,7 @@ noinst_LIBRARIES = libpreprocessor_states.a
 libpreprocessor_states_a_SOURCES = \
 pps_arpspoof.cc \
 pps_bo.cc \
+pps_dcerpc_server.cc \
 pps_dnp3.cc \
 pps_frag3_engine.cc \
 pps_frag3_global.cc \
diff --git a/tools/snort2lua/preprocessor_states/pps_dcerpc_server.cc b/tools/snort2lua/preprocessor_states/pps_dcerpc_server.cc
new file mode 100644 (file)
index 0000000..b6e7024
--- /dev/null
@@ -0,0 +1,625 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+// pps_dcerpc_server.cc author Maya Dagon <mdagon@cisco.com>
+
+#include <sstream>
+#include <vector>
+#include <map>
+#include <cstring>
+
+#include "conversion_state.h"
+#include "helpers/s2l_util.h"
+#include "helpers/util_binder.h"
+
+namespace preprocessors
+{
+namespace
+{
+#define MIN_PORT 0
+#define MAX_PORT 65535
+
+enum DceDetectListState
+{
+    DCE_DETECT_LIST_STATE__START,
+    DCE_DETECT_LIST_STATE__TYPE,
+    DCE_DETECT_LIST_STATE__PORTS_START,
+    DCE_DETECT_LIST_STATE__PORTS_END,
+    DCE_DETECT_LIST_STATE__END,
+};
+
+std::string transport[] = { "smb", "tcp" };
+
+std::map <std::string, std::vector<uint16_t> > default_ports
+{
+    { "smb", { 139, 445 }
+    },
+    { "tcp", { 135 }
+    }
+};
+
+class DcerpcServer : public ConversionState
+{
+public:
+    DcerpcServer(Converter& c);
+    virtual ~DcerpcServer() { }
+    virtual bool convert(std::istringstream& data_stream);
+
+private:
+    bool get_bracket_list(std::istringstream& data_stream, std::string& list);
+    bool convert_val_or_list(std::istringstream& data_stream, std::string& str);
+    bool parse_smb_file_inspection(std::istringstream& data_stream);
+    bool parse_detect(std::istringstream& data_stream, std::map<std::string, Binder*> bind, bool
+        is_detect);
+    void add_default_ports(std::string type, std::map<std::string, Binder*> bind);
+    bool parse_and_add_ports(std::string ports, std::string type,  std::map<std::string,
+        Binder*> bind, bool is_detect);
+    bool parse_nets(std::istringstream& data_stream, std::map<std::string,
+        Binder*> bind);
+    bool add_option_to_all_transports(std::string option, std::string value);
+
+    std::map<std::string, bool> detect_ports_set;
+    std::map<std::string, std::string> table_name;
+    static int binding_id;
+};
+} // namespace
+
+int DcerpcServer::binding_id = 0;
+
+DcerpcServer::DcerpcServer(Converter& c) : ConversionState(c)
+{
+    for (auto type: transport)
+        detect_ports_set[type] = false;
+}
+
+bool DcerpcServer::get_bracket_list(std::istringstream& data_stream, std::string& list)
+{
+    std::string tail;
+    do
+    {
+        if (!(data_stream >> tail))
+        {
+            return false;
+        }
+        list = list + tail;
+    }
+    while (tail.find(']') == std::string::npos);
+
+    return true;
+}
+
+// Read from data_stream either a single value x or list : [x,y,z ... ]
+// Put in str either a single value 'x', or space sperated list 'x y z'
+bool DcerpcServer::convert_val_or_list(std::istringstream& data_stream, std::string& str)
+{
+    if (!(data_stream >> str))
+    {
+        return false;
+    }
+
+    if ((str.find('[') != std::string::npos) &&  (str.find(']') == std::string::npos))
+    {
+        if (!get_bracket_list(data_stream, str))
+        {
+            return false;
+        }
+    }
+
+    if (str.back() == ',')
+        str.pop_back();
+
+    if (str.back() == ']')
+        str.pop_back();
+
+    if (str.front() == '[')
+        str.erase(0,1);
+
+    // remove additional whitespaces
+    str.erase(remove_if(str.begin(), str.end(), isspace), str.end());
+
+    // convert ',' seperators to spaces
+    replace(str.begin(), str.end(), ',', ' ');
+
+    return true;
+}
+
+bool DcerpcServer::parse_smb_file_inspection(std::istringstream& data_stream)
+{
+    bool tmpval = true;
+    std::string file_inspect;
+
+    if (!(data_stream >> file_inspect))
+    {
+        return false;
+    }
+
+    if (file_inspect.find('[') == std::string::npos) //single arg
+    {
+        if (file_inspect.back() == ',')
+        {
+            file_inspect.pop_back();
+        }
+        tmpval = table_api.add_option("smb_file_inspection", file_inspect);
+    }
+    else
+    {
+        if (file_inspect.find(']') == std::string::npos)
+        {
+            if (!get_bracket_list(data_stream, file_inspect))
+            {
+                return false;
+            }
+        }
+
+        size_t pos = file_inspect.find(',');
+        if ((pos == std::string::npos) || (pos <= 1))
+        {
+            return false;
+        }
+
+        std::string arg = file_inspect.substr(1, pos-1);
+        // remove additional whitespaces
+        arg.erase(remove_if(arg.begin(), arg.end(), isspace), arg.end());
+        tmpval = table_api.add_option("smb_file_inspection", arg);
+
+        pos = file_inspect.find("file-depth");
+        if (pos == std::string::npos)
+        {
+            return false;
+        }
+
+        arg = file_inspect.substr(pos + strlen("file-depth"));
+        tmpval = table_api.add_option("smb_file_depth", std::stoi(arg)) && tmpval;
+    }
+
+    return tmpval;
+}
+
+void DcerpcServer::add_default_ports(std::string type,  std::map<std::string,Binder*> bind)
+{
+    for (auto port : default_ports[type])
+    {
+        bind[type]->add_when_port(std::to_string(port));
+    }
+}
+
+// add single port / range
+bool DcerpcServer::parse_and_add_ports(std::string ports, std::string type, std::map<std::string,
+    Binder*> bind, bool is_detect)
+{
+    if (ports.empty())
+    {
+        return true;
+    }
+
+    std::vector<std::string> port_list;
+
+    util::split(ports, ',', port_list);
+    for (std::string port : port_list)
+    {
+        size_t pos = port.find(':');
+        if (pos == std::string::npos)
+        {
+            bind[type]->add_when_port(port);
+        }
+        else
+        {
+            uint16_t min_port = MIN_PORT;
+            uint16_t max_port = MAX_PORT;
+
+            if (pos != 0)
+            {
+                min_port = std::stoi(port.substr(0, pos));
+            }
+
+            if (pos != (port.length()-1))
+            {
+                max_port = std::stoi(port.substr(pos+1));
+            }
+
+            if (max_port < min_port)
+            {
+                return false;
+            }
+
+            for (uint32_t i = min_port; i<= max_port; i++)
+            {
+                bind[type]->add_when_port(std::to_string(i));
+            }
+        }
+    }
+
+    if (is_detect)
+    {
+        detect_ports_set[type] = true;
+    }
+
+    return true;
+}
+
+bool DcerpcServer::parse_detect(std::istringstream& data_stream,
+    std::map<std::string,Binder*> bind, bool is_detect)
+{
+    std::string type;
+    bool one_type = false;
+    DceDetectListState state = DCE_DETECT_LIST_STATE__START;
+
+    while (state != DCE_DETECT_LIST_STATE__END)
+    {
+        switch (state)
+        {
+        case DCE_DETECT_LIST_STATE__START:
+        {
+            char c = data_stream.peek();
+            if (isspace(c))
+            {
+                data_stream.get(c);
+            }
+            else if (c == '[')
+            {
+                data_stream.get(c);
+                state = DCE_DETECT_LIST_STATE__TYPE;
+            }
+            else
+            {
+                one_type = true;
+                state = DCE_DETECT_LIST_STATE__TYPE;
+            }
+        }
+        break;
+
+        case DCE_DETECT_LIST_STATE__TYPE:
+        {
+            if (!(data_stream >> type))
+            {
+                return false;
+            }
+
+            // clear whitespaces
+            type.erase(remove_if(type.begin(), type.end(), isspace), type.end());
+
+            bool use_default_ports = false;
+
+            if (type.back() == ',')
+            {
+                use_default_ports = true;
+                type.pop_back();
+                if (one_type)
+                {
+                    return true;
+                }
+            }
+
+            if (type.back() == ']')
+            {
+                return true;
+            }
+
+            if (!use_default_ports)
+            {
+                state = DCE_DETECT_LIST_STATE__PORTS_START;
+            }
+        }
+        break;
+
+        case DCE_DETECT_LIST_STATE__PORTS_START:
+        {
+            std::string ports;
+
+            if (!(data_stream >> ports))
+            {
+                if (one_type)
+                {
+                    return true;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            if ((ports.find('[') != std::string::npos) &&  (ports.find(']') == std::string::npos))
+            {
+                std::string tail;
+
+                if (!getline(data_stream, tail,']'))
+                {
+                    return false;
+                }
+                ports = ports + tail;
+            }
+
+            if (ports.back() == ',')
+            {
+                ports.pop_back();
+                if (!one_type)
+                {
+                    state = DCE_DETECT_LIST_STATE__TYPE;
+                }
+            }
+
+            size_t pos = ports.find("]]");
+            if ((pos != std::string::npos) ||
+                ((ports.find('[') == std::string::npos) &&  (ports.find(']') !=
+                std::string::npos)))
+            {
+                // found outer list seperator
+                if (one_type)
+                {
+                    return false;
+                }
+                else
+                {
+                    state = DCE_DETECT_LIST_STATE__END;
+                }
+            }
+
+            if (state == DCE_DETECT_LIST_STATE__PORTS_START) // didn't fall under previous
+            {                                                 // conditions
+                state = DCE_DETECT_LIST_STATE__PORTS_END;
+            }
+
+            // if ports are for unsupported types - stop here
+            if (bind.find(type) == bind.end())
+            {
+                continue;
+            }
+
+            // remove '[',']'
+            ports.erase(std::remove(ports.begin(), ports.end(), '['), ports.end());
+            ports.erase(std::remove(ports.begin(), ports.end(), ']'), ports.end());
+            // remove extra spaces
+            ports.erase(remove_if(ports.begin(), ports.end(), isspace), ports.end());
+
+            if (!parse_and_add_ports(ports, type, bind, is_detect))
+            {
+                return false;
+            }
+        }
+        break;
+
+        case DCE_DETECT_LIST_STATE__PORTS_END:
+        {
+            char c;
+
+            if (one_type)
+            {
+                return true;
+            }
+            else // wait for list terminator or item seperator
+            {
+                if (!data_stream.get(c))
+                    return false;
+
+                if (c == ']')
+                {
+                    state = DCE_DETECT_LIST_STATE__END;
+                }
+                else if (c == ',')
+                {
+                    state = DCE_DETECT_LIST_STATE__TYPE;
+                }
+                else if (!isspace(c))
+                {
+                    return false;
+                }
+            }
+        }
+        break;
+
+        default:
+            return false;
+        }
+    }
+    return true;
+}
+
+bool DcerpcServer::parse_nets(std::istringstream& data_stream, std::map<std::string,
+    Binder*> bind)
+{
+    for (auto type : transport)
+    {
+        table_name[type] = "dce_" + type + std::to_string(binding_id);
+        bind[type]->set_use_name(table_name[type]);
+    }
+    binding_id++;
+
+    std::string nets;
+    if (!convert_val_or_list(data_stream, nets))
+    {
+        return false;
+    }
+
+    for (auto type : transport)
+    {
+        bind[type]->add_when_net(nets);
+    }
+
+    return true;
+}
+
+bool DcerpcServer::add_option_to_all_transports(std::string option, std::string value)
+{
+    bool retval = true;
+
+    for (auto type: transport)
+    {
+        table_api.open_table(table_name[type]);
+        retval = table_api.add_option(option, value) && retval;
+        table_api.close_table();
+    }
+
+    return retval;
+}
+
+bool DcerpcServer::convert(std::istringstream& data_stream)
+{
+    std::string keyword;
+    bool retval = true;
+
+    Binder bind_tcp(table_api);
+    Binder bind_smb(table_api);
+
+    std::map<std::string, Binder*> bind;
+
+    bind["smb"] = &bind_smb;
+    bind["tcp"] = &bind_tcp;
+
+    for (auto type : transport)
+    {
+        bind[type]->set_when_proto("tcp");
+        bind[type]->set_use_type("dce_" + type);
+    }
+
+    if (!(data_stream >> keyword))
+        return false;
+
+    if (keyword.back() == ',')
+        keyword.pop_back();
+
+    if (!keyword.compare("default"))
+    {
+        for (auto type : transport)
+            table_name[type] = "dce_" + type;
+    }
+    else
+    {
+        if (keyword.compare("net"))
+        {
+            return false;
+        }
+        if (!parse_nets(data_stream, bind))
+        {
+            return false;
+        }
+    }
+
+    while (data_stream >> keyword)
+    {
+        bool tmpval = true;
+
+        if (keyword.back() == ',')
+            keyword.pop_back();
+
+        if (keyword.empty())
+            continue;
+
+        if (!keyword.compare("policy"))
+        {
+            std::string policy;
+
+            if (!(data_stream >> policy))
+                return false;
+
+            if (policy.back() == ',')
+                policy.pop_back();
+
+            tmpval = add_option_to_all_transports("policy", policy);
+        }
+        else if (!keyword.compare("detect"))
+        {
+            tmpval = parse_detect(data_stream, bind, true);
+        }
+        else if (!keyword.compare("autodetect"))
+        {
+            tmpval = parse_detect(data_stream, bind, false);
+        }
+        else if (!keyword.compare("no_autodetect_http_proxy_ports"))
+        {
+            // FIXIT - add once http transport is supported
+        }
+        else if (!keyword.compare("smb_invalid_shares"))
+        {
+            std::string invalid_shares;
+
+            if (!convert_val_or_list(data_stream, invalid_shares))
+                return false;
+
+            table_api.open_table(table_name["smb"]);
+            tmpval = table_api.add_option("smb_invalid_shares", invalid_shares);
+            table_api.close_table();
+        }
+        else if (!keyword.compare("smb_max_chain"))
+        {
+            table_api.open_table(table_name["smb"]);
+            tmpval = parse_int_option("smb_max_chain", data_stream, false);
+            table_api.close_table();
+        }
+        else if (!keyword.compare("smb_file_inspection"))
+        {
+            table_api.open_table(table_name["smb"]);
+            tmpval = parse_smb_file_inspection(data_stream);
+            table_api.close_table();
+        }
+        else if (!keyword.compare("smb2_max_compound"))
+        {
+            table_api.open_table(table_name["smb"]);
+            tmpval = parse_int_option("smb_max_compound", data_stream, false);
+            table_api.close_table();
+        }
+        else if (!keyword.compare("valid_smb_versions"))
+        {
+            std::string versions;
+
+            if (!convert_val_or_list(data_stream, versions))
+                return false;
+
+            table_api.open_table(table_name["smb"]);
+            tmpval = table_api.add_option("valid_smb_versions", versions);
+            table_api.close_table();
+        }
+        else
+        {
+            tmpval = false;
+        }
+
+        if (!tmpval)
+        {
+            data_api.failed_conversion(data_stream, keyword);
+            retval = false;
+        }
+    }
+
+    for (auto type : transport)
+    {
+        if (!detect_ports_set[type])
+        {
+            add_default_ports(type, bind);
+        }
+    }
+
+    return retval;
+}
+
+/**************************
+ *******  A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& c)
+{
+    return new DcerpcServer(c);
+}
+
+static const ConvertMap preprocessor_dcerpc_server =
+{
+    "dcerpc2_server",
+    ctor,
+};
+
+const ConvertMap* dcerpc_server_map = &preprocessor_dcerpc_server;
+}
+
index b3bd2adf134a5f9d1a3ea8752ccc890040d56048..6d2f31934ceed13881b8966a01fb4342c5fb3895 100644 (file)
@@ -24,6 +24,7 @@ namespace preprocessors
 extern const ConvertMap* arpspoof_map;
 extern const ConvertMap* arpspoof_host_map;
 extern const ConvertMap* bo_map;
+extern const ConvertMap* dcerpc_server_map;
 extern const ConvertMap* dnp3_map;
 extern const ConvertMap* frag3_engine_map;
 extern const ConvertMap* frag3_global_map;
@@ -59,6 +60,7 @@ const std::vector<const ConvertMap*> preprocessor_api =
     arpspoof_map,
     arpspoof_host_map,
     bo_map,
+    dcerpc_server_map,
     dnp3_map,
     frag3_engine_map,
     frag3_global_map,