From: Tom Peters (thopeter) Date: Mon, 23 Aug 2021 20:34:06 +0000 (+0000) Subject: Merge pull request #3027 in SNORT/snort3 from ~THOPETER/snort3:nhttp157 to master X-Git-Tag: 3.1.11.0~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=17b1f9402ed674e98150a197c857f2e651807f5e;p=thirdparty%2Fsnort3.git Merge pull request #3027 in SNORT/snort3 from ~THOPETER/snort3:nhttp157 to master Squashed commit of the following: commit f9c2cf8e5f7832950c20c2aa049ce37c48b78240 Author: Tom Peters Date: Thu Jul 8 16:08:50 2021 -0400 http_inspect: two new built-in rules --- diff --git a/doc/user/http_inspect.txt b/doc/user/http_inspect.txt index 26687bff5..721f19c1c 100755 --- a/doc/user/http_inspect.txt +++ b/doc/user/http_inspect.txt @@ -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 diff --git a/src/service_inspectors/http_inspect/http_cutter.cc b/src/service_inspectors/http_inspect/http_cutter.cc index 848891a7b..c212d8de1 100644 --- a/src/service_inspectors/http_inspect/http_cutter.cc +++ b/src/service_inspectors/http_inspect/http_cutter.cc @@ -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; diff --git a/src/service_inspectors/http_inspect/http_cutter.h b/src/service_inspectors/http_inspect/http_cutter.h index ad6b7df71..2a6fe1f08 100644 --- a/src/service_inspectors/http_inspect/http_cutter.h +++ b/src/service_inspectors/http_inspect/http_cutter.h @@ -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; diff --git a/src/service_inspectors/http_inspect/http_enum.h b/src/service_inspectors/http_inspect/http_enum.h index 4228d2de0..f1c5ff03d 100755 --- a/src/service_inspectors/http_inspect/http_enum.h +++ b/src/service_inspectors/http_inspect/http_enum.h @@ -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, diff --git a/src/service_inspectors/http_inspect/http_module.cc b/src/service_inspectors/http_inspect/http_module.cc index e56b66f90..69ec5f1fa 100755 --- a/src/service_inspectors/http_inspect/http_module.cc +++ b/src/service_inspectors/http_inspect/http_module.cc @@ -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(); diff --git a/src/service_inspectors/http_inspect/http_module.h b/src/service_inspectors/http_inspect/http_module.h index cab31b8a3..3ec9dd4b1 100755 --- a/src/service_inspectors/http_inspect/http_module.h +++ b/src/service_inspectors/http_inspect/http_module.h @@ -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; diff --git a/src/service_inspectors/http_inspect/http_msg_header.cc b/src/service_inspectors/http_inspect/http_msg_header.cc index bc3549ea8..4a808e0bb 100755 --- a/src/service_inspectors/http_inspect/http_msg_header.cc +++ b/src/service_inspectors/http_inspect/http_msg_header.cc @@ -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) diff --git a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc index 7fafbe54e..bf529f9de 100644 --- a/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc +++ b/src/service_inspectors/http_inspect/http_stream_splitter_scan.cc @@ -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); diff --git a/src/service_inspectors/http_inspect/http_tables.cc b/src/service_inspectors/http_inspect/http_tables.cc index e93e4f79a..2107baaf5 100755 --- a/src/service_inspectors/http_inspect/http_tables.cc +++ b/src/service_inspectors/http_inspect/http_tables.cc @@ -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" },