]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #3027 in SNORT/snort3 from ~THOPETER/snort3:nhttp157 to master
authorTom Peters (thopeter) <thopeter@cisco.com>
Mon, 23 Aug 2021 20:34:06 +0000 (20:34 +0000)
committerTom Peters (thopeter) <thopeter@cisco.com>
Mon, 23 Aug 2021 20:34:06 +0000 (20:34 +0000)
Squashed commit of the following:

commit f9c2cf8e5f7832950c20c2aa049ce37c48b78240
Author: Tom Peters <thopeter@cisco.com>
Date:   Thu Jul 8 16:08:50 2021 -0400

    http_inspect: two new built-in rules

doc/user/http_inspect.txt
src/service_inspectors/http_inspect/http_cutter.cc
src/service_inspectors/http_inspect/http_cutter.h
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_header.cc
src/service_inspectors/http_inspect/http_stream_splitter_scan.cc
src/service_inspectors/http_inspect/http_tables.cc

index 26687bff5ce9275a21859bd5eab03309ca286f80..721f19c1cbb8bc6d9681a917a657adcfd1aa5195 100755 (executable)
@@ -207,6 +207,21 @@ inspector as they are defined, e.g "x-forwarded-for" will be preferred than
 "true-client-ip" if both headers are present in the stream. The header names
 should be delimited by a space.
 
+===== maximum_host_length
+
+Setting maximum_host_length causes http_inspect to generate 119:25 if the
+Host header value including optional white space exceeds the specified length.
+In the abnormal case of multiple Host headers, the total length of the combined
+values is used. The default value is -1, meaning do not perform this check.
+
+===== maximum_chunk_length
+
+http_inspect strictly limits individual chunks within a chunked message
+body to be less than four gigabytes.
+
+A lower limit may be configured by setting maximum_chunk_length. Any chunk
+longer than maximum chunk length will generate a 119:16 alert.
+
 ===== URI processing
 
 Normalization and inspection of the URI in the HTTP request message is a
index 848891a7b84e3e235bea1c62d950483022d2bc17..c212d8de16c2e58acf670d90601fb7d7d9d7f362 100644 (file)
@@ -544,13 +544,19 @@ ScanResult HttpBodyChunkCutter::cut(const uint8_t* buffer, uint32_t length,
             else
             {
                 expected = expected * 16 + as_hex[buffer[k]];
-                if (++digits_seen > 8)
+                if ((++digits_seen > 8) || (expected > maximum_chunk_length))
                 {
-                    // overflow protection: must fit into 32 bits
-                    *infractions += INF_CHUNK_TOO_LARGE;
-                    events->create_event(EVENT_BROKEN_CHUNK);
-                    transition_to_chunk_bad(accelerate_this_packet);
-                    k--;
+                    // alert for exceeding configurable limit
+                    *infractions += INF_CHUNK_OVER_MAXIMUM;
+                    events->create_event(EVENT_LARGE_CHUNK);
+                    if (digits_seen > 8)
+                    {
+                        // overflow protection: absolutely must fit into 32 bits
+                        *infractions += INF_CHUNK_TOO_LARGE;
+                        events->create_event(EVENT_BROKEN_CHUNK);
+                        transition_to_chunk_bad(accelerate_this_packet);
+                        k--;
+                    }
                 }
                 if (expected != 0)
                     zero_chunk = false;
index ad6b7df7160c30ee1551014acde38ad55cf7fafb..2a6fe1f08db0199c96e85ebe70bf1e4e266dcae4 100644 (file)
@@ -159,9 +159,10 @@ public:
 class HttpBodyChunkCutter : public HttpBodyCutter
 {
 public:
-    HttpBodyChunkCutter(bool accelerated_blocking, ScriptFinder* finder,
-        HttpEnums::CompressId compression, HttpFlowData* ssn_data) :
-        HttpBodyCutter(accelerated_blocking, finder, compression, ssn_data)
+    HttpBodyChunkCutter(int64_t maximum_chunk_length_, bool accelerated_blocking,
+        ScriptFinder* finder, HttpEnums::CompressId compression, HttpFlowData* ssn_data) :
+        HttpBodyCutter(accelerated_blocking, finder, compression, ssn_data),
+        maximum_chunk_length(maximum_chunk_length_)
         {}
     HttpEnums::ScanResult cut(const uint8_t* buffer, uint32_t length,
         HttpInfractions* infractions, HttpEventGen* events, uint32_t flow_target, bool stretch,
@@ -173,6 +174,8 @@ public:
 private:
     void transition_to_chunk_bad(bool& accelerate_this_packet);
 
+    const int64_t maximum_chunk_length;
+
     uint32_t data_seen = 0;
     HttpEnums::ChunkState curr_state = HttpEnums::CHUNK_NEWLINES;
     uint32_t expected = 0;
index 4228d2de085d3238991104df16cb01831871d0b3..f1c5ff03d2649b9020fe1181f0a12321bcaf68b2 100755 (executable)
@@ -273,6 +273,8 @@ enum Infraction
     INF_JS_SHORTENED_TAG,
     INF_JS_IDENTIFIER_OVERFLOW,
     INF_JS_TMPL_NEST_OVFLOW,
+    INF_CHUNK_OVER_MAXIMUM,
+    INF_LONG_HOST_VALUE,
     INF__MAX_VALUE
 };
 
@@ -283,7 +285,7 @@ enum EventSid
     EVENT_DOUBLE_DECODE = 2,
     EVENT_U_ENCODE = 3,
     EVENT_BARE_BYTE = 4,
-    // EVENT_OBSOLETE_BASE_36 = 5,   // Previously used, do not reuse this number
+    // EVENT_BASE_36 = 5,                        // Retired. Do not reuse this number
     EVENT_UTF_8 = 6,
     EVENT_CODE_POINT_IN_URI = 7,
     EVENT_MULTI_SLASH = 8,
@@ -294,34 +296,34 @@ enum EventSid
     EVENT_LF_WITHOUT_CR = 13,
     EVENT_NON_RFC_CHAR = 14,
     EVENT_OVERSIZE_DIR = 15,
-    // EVENT_LARGE_CHUNK = 16,
-    // EVENT_PROXY_USE = 17,
+    EVENT_LARGE_CHUNK = 16,
+    // EVENT_PROXY_USE = 17,                     // Retired. Do not reuse this number
     EVENT_WEBROOT_DIR = 18,
     EVENT_LONG_HDR = 19,
     EVENT_MAX_HEADERS = 20,
     EVENT_MULTIPLE_CONTLEN = 21,
-    // EVENT_OBSOLETE_CHUNK_SIZE_MISMATCH = 22,   // Previously used, do not reuse this number
-    // EVENT_INVALID_TRUEIP = 23,
+    // EVENT_CHUNK_SIZE_MISMATCH = 22,           // Retired. Do not reuse this number
+    // EVENT_INVALID_TRUEIP = 23,                // Retired. Do not reuse this number
     EVENT_MULTIPLE_HOST_HDRS = 24,
-    // EVENT_LONG_HOSTNAME = 25,
-    // EVENT_EXCEEDS_SPACES = 26,
-    // EVENT_CONSECUTIVE_SMALL_CHUNKS = 27,
+    EVENT_LONG_HOSTNAME = 25,
+    // EVENT_EXCEEDS_SPACES = 26,                // Retired. Do not reuse this number
+    // EVENT_CONSECUTIVE_SMALL_CHUNKS = 27,      // Retired. Do not reuse this number
     EVENT_UNBOUNDED_POST = 28,
-    // EVENT_MULTIPLE_TRUEIP_IN_SESSION = 29,
-    // EVENT_BOTH_TRUEIP_XFF_HDRS = 30,
+    // EVENT_MULTIPLE_TRUEIP_IN_SESSION = 29,    // Retired. Do not reuse this number
+    // EVENT_BOTH_TRUEIP_XFF_HDRS = 30,          // Retired. Do not reuse this number
     EVENT_UNKNOWN_METHOD = 31,
     EVENT_SIMPLE_REQUEST = 32,
     EVENT_UNESCAPED_SPACE_URI = 33,
     EVENT_PIPELINE_MAX = 34,
 
-    // EVENT_OBSOLETE_ANOM_SERVER = 101,      // Previously used, do not reuse this number
+    // EVENT_ANOM_SERVER = 101,                  // Retired. Do not reuse this number
     EVENT_INVALID_STATCODE = 102,
-    // EVENT_UNUSED_1 = 103,
+    // EVENT_UNUSED_1 = 103,                     // Retired. Do not reuse this number
     EVENT_UTF_NORM_FAIL = 104,
     EVENT_UTF7 = 105,
-    // EVENT_DECOMPR_FAILED = 106,
-    // EVENT_CONSECUTIVE_SMALL_CHUNKS_S = 107,
-    // EVENT_UNUSED_2 = 108,
+    // EVENT_DECOMPR_FAILED = 106,               // Retired. Do not reuse this number
+    // EVENT_CONSECUTIVE_SMALL_CHUNKS_S = 107,   // Retired. Do not reuse this number
+    // EVENT_UNUSED_2 = 108,                     // Retired. Do not reuse this number
     EVENT_JS_OBFUSCATION_EXCD = 109,
     EVENT_JS_EXCESS_WS = 110,
     EVENT_MIXED_ENCODINGS = 111,
index e56b66f904fd676914342d0af588f117d8e2a095..69ec5f1faadd1df4b316d82360ae712dca031642 100755 (executable)
@@ -55,6 +55,12 @@ const Parameter HttpModule::http_params[] =
     { "unzip", Parameter::PT_BOOL, nullptr, "true",
       "decompress gzip and deflate message bodies" },
 
+    { "maximum_host_length", Parameter::PT_INT, "-1:max53", "-1",
+      "maximum allowed length for Host header value (-1 no limit)" },
+
+    { "maximum_chunk_length", Parameter::PT_INT, "0:4294967295", "4294967295",
+      "maximum allowed length for a message body chunk" },
+
     { "normalize_utf", Parameter::PT_BOOL, nullptr, "true",
       "normalize charset utf encodings in response bodies" },
 
@@ -192,6 +198,14 @@ bool HttpModule::set(const char*, Value& val, SnortConfig*)
     {
         params->normalize_utf = val.get_bool();
     }
+    else if (val.is("maximum_host_length"))
+    {
+        params->maximum_host_length = val.get_int64();
+    }
+    else if (val.is("maximum_chunk_length"))
+    {
+        params->maximum_chunk_length = val.get_int64();
+    }
     else if (val.is("decompress_pdf"))
     {
         params->decompress_pdf = val.get_bool();
index cab31b8a3c9d5b7803f0d70e8d2ffd8bcc6ac677..3ec9dd4b1912277fcec981bcfc5a622f8c10d45a 100755 (executable)
@@ -42,6 +42,8 @@ public:
 
     bool unzip = true;
     bool normalize_utf = true;
+    int64_t maximum_host_length = -1;
+    int64_t maximum_chunk_length = 0xFFFFFFFF;
     bool decompress_pdf = false;
     bool decompress_swf = false;
     bool decompress_zip = false;
index bc3549ea8a862326ea199335e797ffee06d4ba0f..4a808e0bb695900f0ac94ab5a7ed5fe51426d3e0 100755 (executable)
@@ -150,6 +150,16 @@ void HttpMsgHeader::gen_events()
     if (source_id == SRC_CLIENT)
         get_header_value_norm(HEAD_HOST);
 
+    // Host header value too long
+    if ((params->maximum_host_length != -1) && (source_id == SRC_CLIENT))
+    {
+        if (get_all_header_values_raw(HEAD_HOST).length() > params->maximum_host_length)
+        {
+            add_infraction(INF_LONG_HOST_VALUE);
+            create_event(EVENT_LONG_HOSTNAME);
+        }
+    }
+
     // Content-Transfer-Encoding is a MIME header not sanctioned by HTTP. Which may not prevent
     // some clients from recognizing it and applying a decoding that Snort does not expect.
     if (get_header_count(HEAD_CONTENT_TRANSFER_ENCODING) > 0)
index 7fafbe54e3d0830478a903501e4f29764fbbb77b..bf529f9debd76c7839eb1643b112cc4c8225aef5 100644 (file)
@@ -80,6 +80,7 @@ HttpCutter* HttpStreamSplitter::get_cutter(SectionType type,
             session_data->compression[source_id], session_data);
     case SEC_BODY_CHUNK:
         return (HttpCutter*)new HttpBodyChunkCutter(
+            my_inspector->params->maximum_chunk_length,
             session_data->accelerated_blocking[source_id],
             my_inspector->script_finder,
             session_data->compression[source_id], session_data);
index e93e4f79a349639d1af11a28a0f09bdb0a4f4720..2107baaf5251cb7e4ebb9ce506d0441342ca75d4 100755 (executable)
@@ -203,7 +203,6 @@ const RuleMap HttpModule::http_events[] =
     { EVENT_DOUBLE_DECODE,              "double decoding attack" },
     { EVENT_U_ENCODE,                   "u encoding" },
     { EVENT_BARE_BYTE,                  "bare byte unicode encoding" },
-    // { EVENT_OBSOLETE_BASE_36,           "obsolete event--deleted" },
     { EVENT_UTF_8,                      "UTF-8 encoding" },
     { EVENT_CODE_POINT_IN_URI,          "unicode map code point encoding in URI" },
     { EVENT_MULTI_SLASH,                "multi_slash encoding" },
@@ -214,34 +213,22 @@ const RuleMap HttpModule::http_events[] =
     { EVENT_LF_WITHOUT_CR,              "HTTP header line terminated by LF without a CR" },
     { EVENT_NON_RFC_CHAR,               "non-RFC defined char" },
     { EVENT_OVERSIZE_DIR,               "oversize request-uri directory" },
-    // { EVENT_LARGE_CHUNK,                "oversize chunk encoding" },
-    // { EVENT_PROXY_USE,                  "unauthorized proxy use detected" },
+    { EVENT_LARGE_CHUNK,                "oversize chunk encoding" },
     { EVENT_WEBROOT_DIR,                "webroot directory traversal" },
     { EVENT_LONG_HDR,                   "long header" },
     { EVENT_MAX_HEADERS,                "max header fields" },
     { EVENT_MULTIPLE_CONTLEN,           "multiple content length" },
-    // { EVENT_OBSOLETE_CHUNK_SIZE_MISMATCH, "obsolete event--deleted" },
-    // { EVENT_INVALID_TRUEIP,             "invalid IP in true-client-IP/XFF header" },
     { EVENT_MULTIPLE_HOST_HDRS,         "Host header field appears more than once or has multiple "
                                         "values" },
-    // { EVENT_LONG_HOSTNAME,              "hostname exceeds 255 characters" },
-    // { EVENT_EXCEEDS_SPACES,             "too much whitespace in header (not implemented yet)" },
-    // { EVENT_CONSECUTIVE_SMALL_CHUNKS,   "client consecutive small chunk sizes" },
+    { EVENT_LONG_HOSTNAME,              "Host header value is too long" },
     { EVENT_UNBOUNDED_POST,             "POST or PUT w/o content-length or chunks" },
-    // { EVENT_MULTIPLE_TRUEIP_IN_SESSION, "multiple true ips in a session" },
-    // { EVENT_BOTH_TRUEIP_XFF_HDRS,       "both true-client-IP and XFF hdrs present" },
     { EVENT_UNKNOWN_METHOD,             "unknown method" },
     { EVENT_SIMPLE_REQUEST,             "simple request" },
     { EVENT_UNESCAPED_SPACE_URI,        "unescaped space in HTTP URI" },
     { EVENT_PIPELINE_MAX,               "too many pipelined requests" },
-    // { EVENT_OBSOLETE_ANOM_SERVER,       "obsolete event--deleted" },
     { EVENT_INVALID_STATCODE,           "invalid status code in HTTP response" },
-    // { EVENT_UNUSED_1,                   "unused event number--should not appear" },
     { EVENT_UTF_NORM_FAIL,              "HTTP response has UTF charset that failed to normalize" },
     { EVENT_UTF7,                       "HTTP response has UTF-7 charset" },
-    // { EVENT_DECOMPR_FAILED,             "HTTP response gzip decompression failed" },
-    // { EVENT_CONSECUTIVE_SMALL_CHUNKS_S, "server consecutive small chunk sizes" },
-    // { EVENT_UNUSED_2,                   "unused event number--should not appear" },
     { EVENT_JS_OBFUSCATION_EXCD,        "javascript obfuscation levels exceeds 1" },
     { EVENT_JS_EXCESS_WS,               "javascript whitespaces exceeds max allowed" },
     { EVENT_MIXED_ENCODINGS,            "multiple encodings within javascript obfuscated data" },