From: Philippe Antoine Date: Mon, 9 Sep 2024 07:34:39 +0000 (+0200) Subject: http: have a headers limit X-Git-Tag: suricata-7.0.7~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca8bf6e64c5b6d26672586c5551607ad8bdce0dc;p=thirdparty%2Fsuricata.git http: have a headers limit Ticket: 7191 So as to avoid quadratic complexity in libhtp. Make the limit configurable from suricata.yaml, and have an event when network traffic goes over the limit. (cherry picked from commit bb714c917878ed13aab9e314a026f71570e84f37) --- diff --git a/configure.ac b/configure.ac index 1eaf7c11a3..01c4819647 100644 --- a/configure.ac +++ b/configure.ac @@ -1602,6 +1602,7 @@ AC_CHECK_LIB([htp], [htp_config_set_compression_bomb_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT],[1],[Found htp_config_set_compression_bomb_limit function in libhtp]) ,,[-lhtp]) AC_CHECK_LIB([htp], [htp_config_set_compression_time_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT],[1],[Found htp_config_set_compression_time_limit function in libhtp]) ,,[-lhtp]) AC_CHECK_LIB([htp], [htp_config_set_max_tx],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_MAX_TX],[1],[Found htp_config_set_max_tx function in libhtp]) ,,[-lhtp]) + AC_CHECK_LIB([htp], [htp_config_set_number_headers_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_HEADERS_LIMIT],[1],[Found htp_config_set_number_headers_limit function in libhtp]) ,,[-lhtp]) ]) if test "x$enable_non_bundled_htp" = "xno"; then @@ -1627,6 +1628,7 @@ AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT],[1],[Assuming htp_config_set_compression_bomb_limit function in bundled libhtp]) AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT],[1],[Assuming htp_config_set_compression_time_limit function in bundled libhtp]) AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_MAX_TX],[1],[Assuming htp_config_set_max_tx function in bundled libhtp]) + AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_HEADERS_LIMIT],[1],[Assuming htp_config_set_number_headers_limit function in bundled libhtp]) else echo echo " ERROR: Libhtp is not bundled. Get libhtp by doing:" diff --git a/doc/userguide/configuration/suricata-yaml.rst b/doc/userguide/configuration/suricata-yaml.rst index 6eea5e8793..7bcabdb77f 100644 --- a/doc/userguide/configuration/suricata-yaml.rst +++ b/doc/userguide/configuration/suricata-yaml.rst @@ -1540,6 +1540,10 @@ use of libhtp. #compression-bomb-limit: 1 Mb # Maximum time spent decompressing a single transaction in usec #decompression-time-limit: 100000 + # Maximum number of live transactions per flow + #max-tx: 512 + # Maximum used number of HTTP1 headers in one request or response + #headers-limit: 1024 Other parameters are customizable from Suricata. :: diff --git a/rules/http-events.rules b/rules/http-events.rules index 8c7763f1b6..41f707279e 100644 --- a/rules/http-events.rules +++ b/rules/http-events.rules @@ -91,4 +91,9 @@ alert http any any -> any any (msg:"SURICATA HTTP failed protocol change"; flow: #alert http any any -> any any (msg:"SURICATA HTTP request chunk extension"; flow:established; app-layer-event:http.request_chunk_extension; classtype:protocol-command-decode; sid:2221054; rev:1;) -# next sid 2221055 +#alert http any any -> any any (msg:"SURICATA HTTP request missing protocol"; flow:established,to_server; app-layer-event:http.request_line_missing_protocol; classtype:protocol-command-decode; sid:2221055; rev:1;) + +alert http any any -> any any (msg:"SURICATA HTTP request too many headers"; flow:established,to_server; app-layer-event:http.request_too_many_headers; classtype:protocol-command-decode; sid:2221056; rev:1;) +alert http any any -> any any (msg:"SURICATA HTTP response too many headers"; flow:established,to_client; app-layer-event:http.response_too_many_headers; classtype:protocol-command-decode; sid:2221057; rev:1;) + +# next sid 2221058 diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 7351797046..a02d9202aa 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -168,6 +168,9 @@ SCEnumCharMap http_decoder_event_table[] = { { "RANGE_INVALID", HTTP_DECODER_EVENT_RANGE_INVALID }, { "REQUEST_CHUNK_EXTENSION", HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION }, + { "REQUEST_TOO_MANY_HEADERS", HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS }, + { "RESPONSE_TOO_MANY_HEADERS", HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS }, + /* suricata warnings/errors */ { "MULTIPART_GENERIC_ERROR", HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR }, { "MULTIPART_NO_FILEDATA", HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA }, @@ -642,6 +645,8 @@ struct { { "Ambiguous response C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE }, { "Request chunk extension", HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION }, + { "Too many request headers", HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS }, + { "Too many response headers", HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS }, }; #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0])) @@ -2521,6 +2526,10 @@ static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec) #ifdef HAVE_HTP_CONFIG_SET_MAX_TX #define HTP_CONFIG_DEFAULT_MAX_TX_LIMIT 512 htp_config_set_max_tx(cfg_prec->cfg, HTP_CONFIG_DEFAULT_MAX_TX_LIMIT); +#endif +#ifdef HAVE_HTP_CONFIG_SET_HEADERS_LIMIT +#define HTP_CONFIG_DEFAULT_HEADERS_LIMIT 1024 + htp_config_set_number_headers_limit(cfg_prec->cfg, HTP_CONFIG_DEFAULT_HEADERS_LIMIT); #endif /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set * only the hard limit. So we set both here to the (current) htp defaults. @@ -2884,6 +2893,17 @@ static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s, /* set default soft-limit with our new hard limit */ SCLogConfig("Setting HTTP max-tx limit to %" PRIu32 " bytes", limit); htp_config_set_max_tx(cfg_prec->cfg, (size_t)limit); +#endif +#ifdef HAVE_HTP_CONFIG_SET_HEADERS_LIMIT + } else if (strcasecmp("headers-limit", p->name) == 0) { + uint32_t limit = 0; + if (ParseSizeStringU32(p->val, &limit) < 0) { + FatalError("failed to parse 'headers-limit' " + "from conf file - %s.", + p->val); + } + SCLogConfig("Setting HTTP headers limit to %" PRIu32, limit); + htp_config_set_number_headers_limit(cfg_prec->cfg, limit); #endif } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) { if (!g_disable_randomness) { diff --git a/src/app-layer-htp.h b/src/app-layer-htp.h index f200ea1d39..7a87d49c35 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -129,6 +129,9 @@ enum { HTTP_DECODER_EVENT_RANGE_INVALID, HTTP_DECODER_EVENT_REQUEST_CHUNK_EXTENSION, + HTTP_DECODER_EVENT_REQUEST_TOO_MANY_HEADERS, + HTTP_DECODER_EVENT_RESPONSE_TOO_MANY_HEADERS, + /* suricata errors/warnings */ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR, HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA, diff --git a/suricata.yaml.in b/suricata.yaml.in index 8bfbaac356..ba9005dc3e 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1099,6 +1099,8 @@ app-layer: #decompression-time-limit: 100000 # Maximum number of live transactions per flow #max-tx: 512 + # Maximum used number of HTTP1 headers in one request or response + #headers-limit: 1024 server-config: