diff --git a/configure.ac b/configure.ac index 76fbb7d5e7..48e026817a 100644 --- a/configure.ac +++ b/configure.ac @@ -1611,6 +1611,7 @@ AC_CHECK_LIB([htp], [htp_config_set_response_decompression_layer_limit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT],[1],[Found htp_config_set_response_decompression_layer_limit function in libhtp]) ,,[-lhtp]) AC_EGREP_HEADER(htp_config_set_path_decode_u_encoding, htp/htp.h, AC_DEFINE_UNQUOTED([HAVE_HTP_SET_PATH_DECODE_U_ENCODING],[1],[Found usable htp_config_set_path_decode_u_encoding function in libhtp]) ) AC_CHECK_LIB([htp], [htp_config_set_lzma_memlimit],AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT],[1],[Found htp_config_set_lzma_memlimit function in libhtp]) ,,[-lhtp]) + 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]) ]) if test "x$enable_non_bundled_htp" = "xno"; then @@ -1632,6 +1633,7 @@ # enable when libhtp has been updated AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT],[1],[Assuming htp_config_set_response_decompression_layer_limit function in bundled libhtp]) AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT],[1],[Assuming htp_config_set_lzma_memlimit function in bundled libhtp]) + AC_DEFINE_UNQUOTED([HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT],[1],[Assuming htp_config_set_compression_bomb_limit function in bundled libhtp]) else echo echo " ERROR: Libhtp is not bundled. Get libhtp by doing:" diff --git a/rules/http-events.rules b/rules/http-events.rules index 66ba09cdf5..279f0eea6d 100644 --- a/rules/http-events.rules +++ b/rules/http-events.rules @@ -79,4 +79,6 @@ alert http any any -> any any (msg:"SURICATA HTTP LZMA reached its memory limit" alert http any any -> any any (msg:"SURICATA HTTP duplicate content length field in request"; flow:established,to_server; app-layer-event:http.duplicate_content_length_field_in_request; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221047; rev:1;) alert http any any -> any any (msg:"SURICATA HTTP duplicate content length field in response"; flow:established,to_client; app-layer-event:http.duplicate_content_length_field_in_response; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221048; rev:1;) -# next sid 2221049 +alert http any any -> any any (msg:"SURICATA HTTP compression bomb"; flow:established; app-layer-event:http.compression_bomb; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221049; rev:1;) + +# next sid 2221050 diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 65a3591141..8bc9f12490 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -185,6 +185,8 @@ SCEnumCharMap http_decoder_event_table[ ] = { { "LZMA_MEMLIMIT_REACHED", HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED}, + { "COMPRESSION_BOMB", + HTTP_DECODER_EVENT_COMPRESSION_BOMB}, /* suricata warnings/errors */ { "MULTIPART_GENERIC_ERROR", @@ -560,6 +562,7 @@ struct { { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG}, { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG}, { "C-T multipart/byteranges in responses not supported", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES}, + { "Compression bomb:", HTTP_DECODER_EVENT_COMPRESSION_BOMB}, }; struct { @@ -2379,6 +2382,10 @@ static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec) #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT htp_config_set_lzma_memlimit(cfg_prec->cfg, HTP_CONFIG_DEFAULT_LZMA_MEMLIMIT); +#endif +#ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT + htp_config_set_compression_bomb_limit(cfg_prec->cfg, + HTP_CONFIG_DEFAULT_COMPRESSION_BOMB_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. @@ -2708,6 +2715,21 @@ static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s, if (ConfValIsFalse(p->val)) { htp_config_set_lzma_memlimit(cfg_prec->cfg, 0); } +#endif +#ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT + } else if (strcasecmp("compression-bomb-limit", p->name) == 0) { + uint32_t limit = 0; + if (ParseSizeStringU32(p->val, &limit) < 0) { + FatalError(SC_ERR_SIZE_PARSE, "failed to parse 'compression-bomb-limit' " + "from conf file - %s.", p->val); + } + if (limit == 0) { + FatalError(SC_ERR_SIZE_PARSE, "'compression-bomb-limit' " + "from conf file cannot be 0."); + } + /* set default soft-limit with our new hard limit */ + SCLogConfig("Setting HTTP compression bomb limit to %"PRIu32" bytes", limit); + htp_config_set_compression_bomb_limit(cfg_prec->cfg, (size_t)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 9c24420207..cdb6e99551 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -53,6 +53,7 @@ /* default libhtp lzma limit, taken from libhtp. */ #define HTP_CONFIG_DEFAULT_LZMA_MEMLIMIT 1048576U +#define HTP_CONFIG_DEFAULT_COMPRESSION_BOMB_LIMIT 1048576U #define HTP_CONFIG_DEFAULT_RANDOMIZE 1 #define HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE 10 @@ -125,6 +126,7 @@ enum { HTTP_DECODER_EVENT_REQUEST_BODY_UNEXPECTED, HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED, + HTTP_DECODER_EVENT_COMPRESSION_BOMB, /* suricata errors/warnings */ HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR, diff --git a/suricata.yaml.in b/suricata.yaml.in index 9934761583..a617b00ae3 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -975,9 +975,11 @@ app-layer: double-decode-path: no double-decode-query: no + #lzma-enabled: yes # LZMA decompression memory limit. #lzma-memlimit: 1 Mb - #lzma-enabled: yes + # Compression bomb output limit. + #compression-bomb-limit: 1 Mb server-config: