Version in base suite: 2.9.3-3 Base version: modsecurity-apache_2.9.3-3 Target version: modsecurity-apache_2.9.3-3+deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/m/modsecurity-apache/modsecurity-apache_2.9.3-3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/m/modsecurity-apache/modsecurity-apache_2.9.3-3+deb11u1.dsc changelog | 7 + patches/json_depth_limit.patch | 261 +++++++++++++++++++++++++++++++++++++++++ patches/series | 1 3 files changed, 269 insertions(+) diff -Nru modsecurity-apache-2.9.3/debian/changelog modsecurity-apache-2.9.3/debian/changelog --- modsecurity-apache-2.9.3/debian/changelog 2020-12-10 18:14:15.000000000 +0000 +++ modsecurity-apache-2.9.3/debian/changelog 2021-12-01 15:04:02.000000000 +0000 @@ -1,3 +1,10 @@ +modsecurity-apache (2.9.3-3+deb11u1) bullseye-security; urgency=high + + * Added json_depth_limit.patch + Fixes CVE--2021-42717 + + -- Ervin Hegedus Wed, 01 Dec 2021 16:04:02 +0100 + modsecurity-apache (2.9.3-3) unstable; urgency=medium * Add upstream patch to fix Segfault when using SecRemoteRules. diff -Nru modsecurity-apache-2.9.3/debian/patches/json_depth_limit.patch modsecurity-apache-2.9.3/debian/patches/json_depth_limit.patch --- modsecurity-apache-2.9.3/debian/patches/json_depth_limit.patch 1970-01-01 00:00:00.000000000 +0000 +++ modsecurity-apache-2.9.3/debian/patches/json_depth_limit.patch 2021-12-01 15:04:02.000000000 +0000 @@ -0,0 +1,261 @@ +Description: This patch fixes CVE-2021-42717 + ModSecurity has a DoS Vulnerability in JSON Parsing. The bug + has descibed in CVE-2021-42717. This patch fixes it. +Author: Ervin Hegedus + +--- +Origin: other +Bug: https://github.com/SpiderLabs/ModSecurity/issues/2647 +Last-Update: 2021-12-01 + +--- modsecurity-apache-2.9.3.orig/apache2/apache2_config.c ++++ modsecurity-apache-2.9.3/apache2/apache2_config.c +@@ -50,6 +50,7 @@ void *create_directory_config(apr_pool_t + dcfg->reqbody_inmemory_limit = NOT_SET; + dcfg->reqbody_limit = NOT_SET; + dcfg->reqbody_no_files_limit = NOT_SET; ++ dcfg->reqbody_json_depth_limit = NOT_SET; + dcfg->resbody_access = NOT_SET; + + dcfg->debuglog_name = NOT_SET_P; +@@ -332,6 +333,8 @@ void *merge_directory_configs(apr_pool_t + ? parent->reqbody_limit : child->reqbody_limit); + merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET + ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit); ++ merged->reqbody_json_depth_limit = (child->reqbody_json_depth_limit == NOT_SET ++ ? parent->reqbody_json_depth_limit : child->reqbody_json_depth_limit); + merged->resbody_access = (child->resbody_access == NOT_SET + ? parent->resbody_access : child->resbody_access); + +@@ -648,6 +651,7 @@ void init_directory_config(directory_con + dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; + if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; + if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; ++ if (dcfg->reqbody_json_depth_limit == NOT_SET) dcfg->reqbody_json_depth_limit = REQUEST_BODY_JSON_DEPTH_DEFAULT_LIMIT; + if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; + if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; + if (dcfg->if_limit_action == NOT_SET) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT; +@@ -1920,6 +1924,24 @@ static const char *cmd_request_body_no_f + return NULL; + } + ++static const char *cmd_request_body_json_depth_limit(cmd_parms *cmd, void *_dcfg, ++ const char *p1) ++{ ++ directory_config *dcfg = (directory_config *)_dcfg; ++ long int limit; ++ ++ if (dcfg == NULL) return NULL; ++ ++ limit = strtol(p1, NULL, 10); ++ if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { ++ return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyJsonDepthLimit: %s", p1); ++ } ++ ++ dcfg->reqbody_json_depth_limit = limit; ++ ++ return NULL; ++} ++ + static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, + const char *p1) + { +@@ -3554,6 +3576,14 @@ const command_rec module_directives[] = + ), + + AP_INIT_TAKE1 ( ++ "SecRequestBodyJsonDepthLimit", ++ cmd_request_body_json_depth_limit, ++ NULL, ++ CMD_SCOPE_ANY, ++ "maximum request body JSON parsing depth ModSecurity will accept." ++ ), ++ ++ AP_INIT_TAKE1 ( + "SecRequestEncoding", + cmd_request_encoding, + NULL, +--- modsecurity-apache-2.9.3.orig/apache2/modsecurity.h ++++ modsecurity-apache-2.9.3/apache2/modsecurity.h +@@ -95,6 +95,7 @@ typedef struct msc_parm msc_parm; + #define REQUEST_BODY_DEFAULT_INMEMORY_LIMIT 131072 + #define REQUEST_BODY_DEFAULT_LIMIT 134217728 + #define REQUEST_BODY_NO_FILES_DEFAULT_LIMIT 1048576 ++#define REQUEST_BODY_JSON_DEPTH_DEFAULT_LIMIT 10000 + #define RESPONSE_BODY_DEFAULT_LIMIT 524288 + #define RESPONSE_BODY_HARD_LIMIT 1073741824L + +@@ -492,6 +493,7 @@ struct directory_config { + long int reqbody_inmemory_limit; + long int reqbody_limit; + long int reqbody_no_files_limit; ++ long int reqbody_json_depth_limit; + int resbody_access; + + long int of_limit; +--- modsecurity-apache-2.9.3.orig/apache2/msc_json.c ++++ modsecurity-apache-2.9.3/apache2/msc_json.c +@@ -164,6 +164,11 @@ static int yajl_start_array(void *ctx) { + else { + msr->json->prefix = apr_pstrdup(msr->mp, msr->json->current_key); + } ++ msr->json->current_depth++; ++ if (msr->json->current_depth > msr->txcfg->reqbody_json_depth_limit) { ++ msr->json->depth_limit_exceeded = 1; ++ return 0; ++ } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "New JSON hash context (prefix '%s')", msr->json->prefix); +@@ -200,6 +205,7 @@ static int yajl_end_array(void *ctx) { + */ + msr->json->prefix = (unsigned char *) NULL; + } ++ msr->json->current_depth--; + + return 1; + } +@@ -229,6 +235,11 @@ static int yajl_start_map(void *ctx) + else { + msr->json->prefix = apr_pstrdup(msr->mp, msr->json->current_key); + } ++ msr->json->current_depth++; ++ if (msr->json->current_depth > msr->txcfg->reqbody_json_depth_limit) { ++ msr->json->depth_limit_exceeded = 1; ++ return 0; ++ } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "New JSON hash context (prefix '%s')", msr->json->prefix); +@@ -270,6 +281,7 @@ static int yajl_end_map(void *ctx) + msr->json->current_key = msr->json->prefix; + msr->json->prefix = (unsigned char *) NULL; + } ++ msr->json->current_depth--; + + return 1; + } +@@ -308,6 +320,9 @@ int json_init(modsec_rec *msr, char **er + msr->json->prefix = (unsigned char *) NULL; + msr->json->current_key = (unsigned char *) NULL; + ++ msr->json->current_depth = 0; ++ msr->json->depth_limit_exceeded = 0; ++ + /** + * yajl initialization + * +@@ -337,7 +352,11 @@ int json_process_chunk(modsec_rec *msr, + msr->json->status = yajl_parse(msr->json->handle, buf, size); + if (msr->json->status != yajl_status_ok) { + /* We need to free the yajl error message later, how to do this? */ +- *error_msg = yajl_get_error(msr->json->handle, 0, buf, size); ++ if (msr->json->depth_limit_exceeded) { ++ *error_msg = "JSON depth limit exceeded"; ++ } else { ++ *error_msg = yajl_get_error(msr->json->handle, 0, NULL, 0); ++ } + return -1; + } + +@@ -357,7 +376,11 @@ int json_complete(modsec_rec *msr, char + msr->json->status = yajl_complete_parse(msr->json->handle); + if (msr->json->status != yajl_status_ok) { + /* We need to free the yajl error message later, how to do this? */ +- *error_msg = yajl_get_error(msr->json->handle, 0, NULL, 0); ++ if (msr->json->depth_limit_exceeded) { ++ *error_msg = "JSON depth limit exceeded"; ++ } else { ++ *error_msg = yajl_get_error(msr->json->handle, 0, NULL, 0); ++ } + return -1; + } + +--- modsecurity-apache-2.9.3.orig/apache2/msc_json.h ++++ modsecurity-apache-2.9.3/apache2/msc_json.h +@@ -40,6 +40,8 @@ struct json_data { + /* prefix is used to create data hierarchy (i.e., 'parent.child.value') */ + unsigned char *prefix; + unsigned char *current_key; ++ long int current_depth; ++ int depth_limit_exceeded; + }; + + /* Functions */ +--- modsecurity-apache-2.9.3.orig/tests/regression/rule/15-json.t ++++ modsecurity-apache-2.9.3/tests/regression/rule/15-json.t +@@ -156,5 +156,74 @@ + ), + ), + ), ++}, ++{ ++ type => "rule", ++ comment => "json parser - parsing depth not exceeded", ++ conf => qq( ++ SecRuleEngine On ++ SecRequestBodyAccess On ++ SecDebugLog $ENV{DEBUG_LOG} ++ SecDebugLogLevel 9 ++ SecRequestBodyJsonDepthLimit 5 ++ SecRule REQUEST_HEADERS:Content-Type "application/json" \\ ++ "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" ++ SecRule REQBODY_ERROR "!\@eq 0" "id:'200442',phase:2,log,deny,status:403,msg:'Failed to parse request body'" ++ ), ++ match_log => { ++ debug => [ qr/key/s, 1 ], ++ }, ++ match_response => { ++ status => qr/^200$/, ++ }, ++ request => new HTTP::Request( ++ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ++ [ ++ "Content-Type" => "application/json", ++ ], ++ normalize_raw_request_data( ++ q( ++ { ++ "key1":{"key2":{"key3":{"key4":{"key5":"thevalue"}}}} ++ } ++ ), ++ ), ++ ), ++}, ++{ ++ type => "rule", ++ comment => "json parser - parsing depth exceeded", ++ conf => qq( ++ SecRuleEngine On ++ SecRequestBodyAccess On ++ SecDebugLog $ENV{DEBUG_LOG} ++ SecAuditEngine RelevantOnly ++ SecAuditLog "$ENV{AUDIT_LOG}" ++ SecDebugLogLevel 9 ++ SecRequestBodyJsonDepthLimit 3 ++ SecRule REQUEST_HEADERS:Content-Type "application/json" \\ ++ "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" ++ SecRule REQBODY_ERROR "!\@eq 0" "id:'200443',phase:2,log,deny,status:403,msg:'Failed to parse request body'" ++ ), ++ match_log => { ++ audit => [ qr/JSON parsing error: JSON depth limit exceeded/s, 1 ], ++ }, ++ match_response => { ++ status => qr/^403$/, ++ }, ++ request => new HTTP::Request( ++ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ++ [ ++ "Content-Type" => "application/json", ++ ], ++ normalize_raw_request_data( ++ q( ++ { ++ "key1":{"key2":{"key3":{"key4":{"key5":"thevalue"}}}} ++ } ++ ), ++ ), ++ ), + } + ++ diff -Nru modsecurity-apache-2.9.3/debian/patches/series modsecurity-apache-2.9.3/debian/patches/series --- modsecurity-apache-2.9.3/debian/patches/series 2020-12-10 18:13:10.000000000 +0000 +++ modsecurity-apache-2.9.3/debian/patches/series 2021-12-01 15:04:02.000000000 +0000 @@ -1,3 +1,4 @@ debian_log_dir.patch improve_defaults.patch 970833_fix.patch +json_depth_limit.patch