]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3608: allowed and disallowed methods
authorTom Peters (thopeter) <thopeter@cisco.com>
Tue, 4 Oct 2022 16:23:16 +0000 (16:23 +0000)
committerTom Peters (thopeter) <thopeter@cisco.com>
Tue, 4 Oct 2022 16:23:16 +0000 (16:23 +0000)
Merge in SNORT/snort3 from ~ADMAMOLE/snort3:method to master

Squashed commit of the following:

commit 62f3acf8011d7002eca476b34764e12f8a60edb5
Author: Adrian Mamolea <admamole@cisco.com>
Date:   Thu Aug 18 11:19:30 2022 -0400

    http_inspect: allowed and disallowed methods

doc/reference/builtin_stubs.txt
doc/user/http_inspect.txt
src/service_inspectors/http_inspect/http_enum.h
src/service_inspectors/http_inspect/http_module.cc
src/service_inspectors/http_inspect/http_module.h
src/service_inspectors/http_inspect/http_msg_request.cc
src/service_inspectors/http_inspect/http_tables.cc
src/service_inspectors/http_inspect/test/http_module_test.cc
src/service_inspectors/http_inspect/test/http_uri_norm_test.cc

index 00d475f2d01150a86c603e9f524ed7aca3d48cea..8f60f6c5dd8653deddc623a083a1fecf937b599c 100644 (file)
@@ -1326,6 +1326,10 @@ HTTP message Request-Line longer than 63780 bytes
 
 HTTP/2 preface received instead of an HTTP/1 method
 
+119:287
+
+HTTP request method is not on allowed methods list or is on disallowed methods list.
+
 121:1
 
 Invalid flag set on HTTP/2 frame header
index b0f07f9946c397a47a31ed157d8d5702a2618849..bdcb66ba0e00646cf7f84e535f29f43b4a276561 100755 (executable)
@@ -130,6 +130,27 @@ depth parameter entirely because that is the default.
 These limits have no effect on how much data is forwarded to file
 processing.
 
+===== allowed_methods and disallowed_methods
+
+When either of these options are set, HTTP inspector will check if the method
+in the HTTP request is allowed and if not raise alert 119:287. You can either
+define a list of allowed methods or a list of disallowed methods. Defining
+both is a configuration error. When a list of disallowed methods is defined,
+any method not present on that list is implicitly allowed. Methods on either
+of these lists are considered known methods and will not raise alert 119:31.
+For example if configured for:
+
+    allowed_methods = "GET,PUT,BLUE"
+
+HTTP inspector will raise 119:287 for POST and RED, no alert 119:31 will
+be raised for BLUE, and 119:31 will be raised for RED.
+If configured for
+
+    disallowed_methods = "POST,RED"
+
+HTTP inspector will raise 119:287 for POST and RED, 119:31 for BLUE, and
+no alert 119:31 will be raised for RED.
+
 ===== script_detection
 
 Script detection is a feature that enables Snort to more quickly detect and
index ffbdc80ec1f048afe0d6108bb748b4ebcaca6b01..487a3efd0678d455a90bb31065026ea7fec67266 100755 (executable)
@@ -299,6 +299,8 @@ enum Infraction
     INF_INVALID_SUBVERSION = 133,
     INF_VERSION_0 = 134,
     INF_GZIP_FEXTRA = 135,
+    INF_METHOD_NOT_ON_ALLOWED_LIST = 136,
+    INF_METHOD_ON_DISALLOWED_LIST = 137,
     INF__MAX_VALUE
 };
 
@@ -445,6 +447,7 @@ enum EventSid
     EVENT_PARTIAL_START = 284,
     EVENT_REQ_TOO_LONG = 285,
     EVENT_UNEXPECTED_H2_PREFACE = 286,
+    EVENT_DISALLOWED_METHOD = 287,
     EVENT__MAX_VALUE
 };
 
index a5ac07f135a69f11f23c570b453af9d1d3e0337c..672d88377cba594949574b5a0cf685e7fa85b88f 100755 (executable)
@@ -175,6 +175,12 @@ const Parameter HttpModule::http_params[] =
       "make HTTP/2 request message bodies available for application detection "
           "(detection requires AppId)" },
 
+    { "allowed_methods", Parameter::PT_STRING, nullptr, nullptr,
+      "list of allowed methods" },
+
+    { "disallowed_methods", Parameter::PT_STRING, nullptr, nullptr,
+      "list of disallowed methods" },
+
 #ifdef REG_TEST
     { "test_input", Parameter::PT_BOOL, nullptr, "false",
       "read HTTP messages from text file" },
@@ -234,6 +240,14 @@ bool HttpModule::begin(const char* fqn, int, SnortConfig*)
     return true;
 }
 
+static void store_tokens(Value& val, std::set<std::string>& methods)
+{
+    val.set_first_token();
+    std::string tok;
+    while (val.get_next_csv_token(tok))
+        methods.insert(tok);
+}
+
 bool HttpModule::set(const char*, Value& val, SnortConfig*)
 {
     if (val.is("request_depth"))
@@ -424,6 +438,10 @@ bool HttpModule::set(const char*, Value& val, SnortConfig*)
     {
         params->publish_request_body = val.get_bool();
     }
+    else if (val.is("allowed_methods"))
+        store_tokens(val, params->allowed_methods);
+    else if (val.is("disallowed_methods"))
+        store_tokens(val, params->disallowed_methods);
 
 #ifdef REG_TEST
     else if (val.is("test_input"))
@@ -484,6 +502,9 @@ bool HttpModule::end(const char* fqn, int, SnortConfig*)
     if (strcmp(fqn, "http_inspect"))
         return true;
 
+    if (!params->allowed_methods.empty() && !params->disallowed_methods.empty())
+        ParseError("allowed methods can't be used in conjunction with disallowed methods");
+
     if (!params->uri_param.utf8 && params->uri_param.utf8_bare_byte)
     {
         ParseWarning(WARN_CONF, "Meaningless to do bare byte when not doing UTF-8");
index 7b0070c818c65bf7a7133a8d42b1399d2dd6aaf9..455c8bcc6eac1a83296b742374eac7af809e0f64 100755 (executable)
@@ -21,6 +21,7 @@
 #define HTTP_MODULE_H
 
 #include <bitset>
+#include <set>
 #include <string>
 #include <unordered_set>
 
@@ -121,6 +122,9 @@ public:
     // any custom headers mapped with the their respective Header IDs.
     StrCode header_list[HttpEnums::HEAD__MAX_VALUE + HttpEnums::MAX_CUSTOM_HEADERS + 1] = {};
 
+    std::set<std::string> allowed_methods;
+    std::set<std::string> disallowed_methods;
+
 #ifdef REG_TEST
     int64_t print_amount = 1200;
 
index afc591d2f83b55ab6e4c4ea19d7211f2c98da460..1895673a48c1f0d8d2e907895f05c2c339209476 100644 (file)
@@ -257,7 +257,36 @@ void HttpMsgRequest::gen_events()
         }
     }
 
-    if (method_id == METH__OTHER)
+    bool known_method = false;
+    assert(method.length() > 0);
+    if (!params->allowed_methods.empty() or !params->disallowed_methods.empty())
+    {
+        string method_str((const char*)method.start(), method.length());
+
+        if (!params->allowed_methods.empty())
+        {
+            const set<string>::iterator it = params->allowed_methods.find(method_str);
+            if (it == params->allowed_methods.end())
+            {
+                add_infraction(INF_METHOD_NOT_ON_ALLOWED_LIST);
+                create_event(EVENT_DISALLOWED_METHOD);
+            }
+            else
+                known_method = true;
+        }
+        else
+        {
+            const set<string>::iterator it = params->disallowed_methods.find(method_str);
+            if (it != params->disallowed_methods.end())
+            {
+                add_infraction(INF_METHOD_ON_DISALLOWED_LIST);
+                create_event(EVENT_DISALLOWED_METHOD);
+                known_method = true;
+            }
+        }
+    }
+
+    if (method_id == METH__OTHER && !known_method)
         create_event(EVENT_UNKNOWN_METHOD);
 
     if (uri && uri->get_scheme().length() > LONG_SCHEME_LENGTH)
index ff7dc6f1e5a6a8fa512c14a67b657603e04010a4..e3242dfbce4c85852d7a4b84b5bebd148f30b015 100755 (executable)
@@ -350,6 +350,8 @@ const RuleMap HttpModule::http_events[] =
     { EVENT_PARTIAL_START,              "partial start line" },
     { EVENT_REQ_TOO_LONG,               "HTTP message request line longer than 63780 bytes" },
     { EVENT_UNEXPECTED_H2_PREFACE,      "HTTP/2 preface received instead of an HTTP/1 method" },
+    { EVENT_DISALLOWED_METHOD,          "HTTP request method is not on allowed methods list or is on "
+                                        "disallowed methods list" },
     { 0, nullptr }
 };
 
index 3bbd67111494596b4ed1bcdd5998d158d8a37bb1..de53244082dee98e0bd386c88011a9d097c348c6 100755 (executable)
@@ -46,6 +46,7 @@ void ParseError(const char*, ...) {}
 
 void Value::get_bits(std::bitset<256ul>&) const {}
 void Value::set_first_token() {}
+bool Value::get_next_csv_token(std::string&) { return false; }
 bool Value::get_next_token(std::string& ) { return false; }
 
 int DetectionEngine::queue_event(unsigned int, unsigned int) { return 0; }
index 62fa8f0f34301d87f20478108594bdcad2512550..f6f6e17e5943699f220f86059a68e5f574e1db8f 100755 (executable)
@@ -42,6 +42,7 @@ void ParseWarning(WarningGroup, const char*, ...) {}
 void ParseError(const char*, ...) {}
 void Value::get_bits(std::bitset<256ul>&) const {}
 void Value::set_first_token() {}
+bool Value::get_next_csv_token(std::string&) { return false; }
 bool Value::get_next_token(std::string& ) { return false; }
 int DetectionEngine::queue_event(unsigned int, unsigned int) { return 0; }
 LiteralSearch::Handle* LiteralSearch::setup() { return nullptr; }