diff --git a/CVE-2022-48279.patch b/CVE-2022-48279.patch deleted file mode 100644 index ee54388eb8f96c27d63a656beac03e9904c3b545..0000000000000000000000000000000000000000 --- a/CVE-2022-48279.patch +++ /dev/null @@ -1,405 +0,0 @@ -From 7a489bd07c66d3df19a320b4306e00c49716dbb0 Mon Sep 17 00:00:00 2001 -From: Martin Vierula -Date: Wed, 7 Sep 2022 11:09:47 -0700 -Subject: [PATCH] Multipart parsing fixes and new MULTIPART_PART_HEADERS - collection - ---- - CHANGES | 2 + - apache2/msc_multipart.c | 148 ++++++++++++++------ - apache2/msc_multipart.h | 19 +++ - apache2/re_variables.c | 57 ++++++++ - tests/regression/misc/00-multipart-parser.t | 45 ++++++ - 5 files changed, 230 insertions(+), 41 deletions(-) - -diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c -index d087c863e..4128ab17e 100644 ---- a/apache2/msc_multipart.c -+++ b/apache2/msc_multipart.c -@@ -325,7 +325,14 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { - } - - msr->mpd->mpp_state = 1; -+ msr->mpd->mpp_substate_part_data_read = 0; - msr->mpd->mpp->last_header_name = NULL; -+ -+ /* Record the last part header line in the collection */ -+ if (msr->mpd->mpp->last_header_line != NULL) { -+ *(char **)apr_array_push(msr->mpd->mpp->header_lines) = msr->mpd->mpp->last_header_line; -+ msr_log(msr, 9, "Multipart: Added part header line \"%s\"", msr->mpd->mpp->last_header_line); -+ } - } else { - /* Header line. */ - -@@ -379,12 +386,28 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long."); - return -1; - } -+ if ((msr->mpd->mpp->last_header_line != NULL) && (msr->mpd->mpp->last_header_name != NULL) -+ && (new_value != NULL)) { -+ msr->mpd->mpp->last_header_line = apr_psprintf(msr->mp, -+ "%s: %s", msr->mpd->mpp->last_header_name, new_value); -+ } -+ - } else { - char *header_name, *header_value, *data; - - /* new header */ - -+ /* Record the most recently-seen part header line in the collection */ -+ if (msr->mpd->mpp->last_header_line != NULL) { -+ *(char **)apr_array_push(msr->mpd->mpp->header_lines) = msr->mpd->mpp->last_header_line; -+ msr_log(msr, 9, "Multipart: Added part header line \"%s\"", msr->mpd->mpp->last_header_line); -+ } -+ - data = msr->mpd->buf; -+ -+ msr->mpd->mpp->last_header_line = apr_pstrdup(msr->mp, data); -+ remove_lf_crlf_inplace(msr->mpd->mpp->last_header_line); -+ - while((*data != ':') && (*data != '\0')) data++; - if (*data == '\0') { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.", -@@ -438,6 +461,8 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { - if (error_msg == NULL) return -1; - *error_msg = NULL; - -+ msr->mpd->mpp_substate_part_data_read = 1; -+ - /* Preserve some bytes for later. */ - if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 1) - && (*(p - 1) == '\n') ) -@@ -680,10 +705,14 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err - if (msr->mpd->mpp == NULL) return -1; - msr->mpd->mpp->type = MULTIPART_FORMDATA; - msr->mpd->mpp_state = 0; -+ msr->mpd->mpp_substate_part_data_read = 0; - - msr->mpd->mpp->headers = apr_table_make(msr->mp, 10); - if (msr->mpd->mpp->headers == NULL) return -1; - msr->mpd->mpp->last_header_name = NULL; -+ msr->mpd->mpp->last_header_line = NULL; -+ msr->mpd->mpp->header_lines = apr_array_make(msr->mp, 2, sizeof(char *)); -+ if (msr->mpd->mpp->header_lines == NULL) return -1; - - msr->mpd->reserve[0] = 0; - msr->mpd->reserve[1] = 0; -@@ -983,6 +1012,19 @@ int multipart_complete(modsec_rec *msr, char **error_msg) { - && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') - && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary) + 1) == '-') ) - { -+ if ((msr->mpd->crlf_state_buf_end == 2) && (msr->mpd->flag_lf_line != 1)) { -+ msr->mpd->flag_lf_line = 1; -+ if (msr->mpd->flag_crlf_line) { -+ msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); -+ } else { -+ msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); -+ } -+ } -+ if (msr->mpd->mpp_substate_part_data_read == 0) { -+ /* it looks like the final boundary, but it's where part data should begin */ -+ msr->mpd->flag_invalid_part = 1; -+ msr_log(msr, 4, "Multipart: Warning: Invalid part (data contains final boundary)"); -+ } - /* Looks like the final boundary - process it. */ - if (multipart_process_boundary(msr, 1 /* final */, error_msg) < 0) { - msr->mpd->flag_error = 1; -@@ -1075,54 +1117,63 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, - if ( (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2) - && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) - { -- char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); -- int is_final = 0; -+ if (msr->mpd->crlf_state_buf_end == 2) { -+ msr->mpd->flag_lf_line = 1; -+ } -+ if ((msr->mpd->mpp_substate_part_data_read == 0) && (msr->mpd->boundary_count > 0)) { -+ /* string matches our boundary, but it's where part data should begin */ -+ msr->mpd->flag_invalid_part = 1; -+ msr_log(msr, 4, "Multipart: Warning: Invalid part (data contains boundary)"); -+ } else { -+ char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); -+ int is_final = 0; -+ -+ /* Is this the final boundary? */ -+ if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { -+ is_final = 1; -+ boundary_end += 2; -+ -+ if (msr->mpd->is_complete != 0) { -+ msr->mpd->flag_error = 1; -+ *error_msg = apr_psprintf(msr->mp, -+ "Multipart: Invalid boundary (final duplicate)."); -+ return -1; -+ } -+ } - -- /* Is this the final boundary? */ -- if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { -- is_final = 1; -- boundary_end += 2; -+ /* Allow for CRLF and LF line endings. */ -+ if ( ( (*boundary_end == '\r') -+ && (*(boundary_end + 1) == '\n') -+ && (*(boundary_end + 2) == '\0') ) -+ || ( (*boundary_end == '\n') -+ && (*(boundary_end + 1) == '\0') ) ) -+ { -+ if (*boundary_end == '\n') { -+ msr->mpd->flag_lf_line = 1; -+ } else { -+ msr->mpd->flag_crlf_line = 1; -+ } - -- if (msr->mpd->is_complete != 0) { -- msr->mpd->flag_error = 1; -- *error_msg = apr_psprintf(msr->mp, -- "Multipart: Invalid boundary (final duplicate)."); -- return -1; -- } -- } -+ if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { -+ msr->mpd->flag_error = 1; -+ return -1; -+ } - -- /* Allow for CRLF and LF line endings. */ -- if ( ( (*boundary_end == '\r') -- && (*(boundary_end + 1) == '\n') -- && (*(boundary_end + 2) == '\0') ) -- || ( (*boundary_end == '\n') -- && (*(boundary_end + 1) == '\0') ) ) -- { -- if (*boundary_end == '\n') { -- msr->mpd->flag_lf_line = 1; -- } else { -- msr->mpd->flag_crlf_line = 1; -- } -+ if (is_final) { -+ msr->mpd->is_complete = 1; -+ } - -- if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { -+ processed_as_boundary = 1; -+ msr->mpd->boundary_count++; -+ } -+ else { -+ /* error */ - msr->mpd->flag_error = 1; -+ *error_msg = apr_psprintf(msr->mp, -+ "Multipart: Invalid boundary: %s", -+ log_escape_nq(msr->mp, msr->mpd->buf)); - return -1; - } -- -- if (is_final) { -- msr->mpd->is_complete = 1; -- } -- -- processed_as_boundary = 1; -- msr->mpd->boundary_count++; -- } -- else { -- /* error */ -- msr->mpd->flag_error = 1; -- *error_msg = apr_psprintf(msr->mp, -- "Multipart: Invalid boundary: %s", -- log_escape_nq(msr->mp, msr->mpd->buf)); -- return -1; - } - } else { /* It looks like a boundary but we couldn't match it. */ - char *p = NULL; -@@ -1221,6 +1272,21 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, - msr->mpd->bufptr = msr->mpd->buf; - msr->mpd->bufleft = MULTIPART_BUF_SIZE; - msr->mpd->buf_contains_line = (c == 0x0a) ? 1 : 0; -+ -+ if (c == 0x0a) { -+ if (msr->mpd->crlf_state == 1) { -+ msr->mpd->crlf_state = 3; -+ } else { -+ msr->mpd->crlf_state = 2; -+ } -+ } -+ msr->mpd->crlf_state_buf_end = msr->mpd->crlf_state; -+ } -+ -+ if (c == 0x0d) { -+ msr->mpd->crlf_state = 1; -+ } else if (c != 0x0a) { -+ msr->mpd->crlf_state = 0; - } - - if ((msr->mpd->is_complete) && (inleft != 0)) { -diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h -index a0f6a08dd..13db0658f 100644 ---- a/apache2/msc_multipart.h -+++ b/apache2/msc_multipart.h -@@ -55,6 +55,8 @@ struct multipart_part { - - char *last_header_name; - apr_table_t *headers; -+ char *last_header_line; -+ apr_array_header_t *header_lines; - - unsigned int offset; - unsigned int length; -@@ -81,6 +83,15 @@ struct multipart_data { - char *bufptr; - int bufleft; - -+ /* line ending status seen immediately before current position. -+ * 0 = neither LF nor CR; 1 = prev char CR; 2 = prev char LF alone; -+ * 3 = prev two chars were CRLF -+ */ -+ int crlf_state; -+ -+ /* crlf_state at end of previous buffer */ -+ int crlf_state_buf_end; -+ - unsigned int buf_offset; - - /* pointer that keeps track of a part while -@@ -94,6 +105,14 @@ struct multipart_data { - */ - int mpp_state; - -+ /* part parsing substate; if mpp_state is 1 (collecting -+ * data), then for this variable: -+ * 0 means we have not yet read any data between the -+ * post-headers blank line and the next boundary -+ * 1 means we have read at some data after that blank line -+ */ -+ int mpp_substate_part_data_read; -+ - /* because of the way this parsing algorithm - * works we hold back the last two bytes of - * each data chunk so that we can discard it -diff --git a/apache2/re_variables.c b/apache2/re_variables.c -index 400738615..f3015acd9 100644 ---- a/apache2/re_variables.c -+++ b/apache2/re_variables.c -@@ -1394,6 +1394,52 @@ static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre - return 1; - } - -+/* MULTIPART_PART_HEADERS */ -+ -+static int var_multipart_part_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, -+ apr_table_t *vartab, apr_pool_t *mptmp) -+{ -+ multipart_part **parts = NULL; -+ int i, j, count = 0; -+ -+ if (msr->mpd == NULL) return 0; -+ -+ parts = (multipart_part **)msr->mpd->parts->elts; -+ for(i = 0; i < msr->mpd->parts->nelts; i++) { -+ int match = 0; -+ -+ /* Figure out if we want to include this variable. */ -+ if (var->param == NULL) match = 1; -+ else { -+ if (var->param_data != NULL) { /* Regex. */ -+ char *my_error_msg = NULL; -+ if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, -+ strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; -+ } else { /* Simple comparison. */ -+ if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; -+ } -+ } -+ -+ /* If we had a match add this argument to the collection. */ -+ if (match) { -+ for (j = 0; j < parts[i]->header_lines->nelts; j++) { -+ char *header_line = ((char **)parts[i]->header_lines->elts)[j]; -+ msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); -+ -+ rvar->value = header_line; -+ rvar->value_len = strlen(rvar->value); -+ rvar->name = apr_psprintf(mptmp, "MULTIPART_PART_HEADERS:%s", -+ log_escape_nq(mptmp, parts[i]->name)); -+ apr_table_addn(vartab, rvar->name, (void *)rvar); -+ -+ count++; -+ } -+ } -+ } -+ -+ return count; -+} -+ - /* MODSEC_BUILD */ - - static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, -@@ -2966,6 +3012,17 @@ void msre_engine_register_default_variables(msre_engine *engine) { - PHASE_REQUEST_BODY - ); - -+ /* MULTIPART_PART_HEADERS */ -+ msre_engine_variable_register(engine, -+ "MULTIPART_PART_HEADERS", -+ VAR_LIST, -+ 0, 1, -+ var_generic_list_validate, -+ var_multipart_part_headers_generate, -+ VAR_CACHE, -+ PHASE_REQUEST_BODY -+ ); -+ - /* GEO */ - msre_engine_variable_register(engine, - "GEO", -diff --git a/tests/regression/misc/00-multipart-parser.t b/tests/regression/misc/00-multipart-parser.t -index 3c1f41b7d..e5ee4c13c 100644 ---- a/tests/regression/misc/00-multipart-parser.t -+++ b/tests/regression/misc/00-multipart-parser.t -@@ -1849,3 +1849,48 @@ - ), - }, - -+# part headers -+{ -+ type => "misc", -+ comment => "multipart parser (part headers)", -+ conf => qq( -+ SecRuleEngine On -+ SecDebugLog $ENV{DEBUG_LOG} -+ SecDebugLogLevel 9 -+ SecRequestBodyAccess On -+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:400,id:500168" -+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:400,id:500169" -+ SecRule MULTIPART_PART_HEADERS:image "\@rx content-type:.*jpeg" "phase:2,deny,status:403,id:500170,t:lowercase" -+ ), -+ match_log => { -+ debug => [ qr/500170.*against MULTIPART_PART_HEADERS:image.*Rule returned 1./s, 1 ], -+ }, -+ match_response => { -+ status => qr/^403$/, -+ }, -+ request => new HTTP::Request( -+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", -+ [ -+ "Content-Type" => q(multipart/form-data; boundary=0000), -+ ], -+ normalize_raw_request_data( -+ q( -+ --0000 -+ Content-Disposition: form-data; name="username" -+ -+ Bill -+ --0000 -+ Content-Disposition: form-data; name="email" -+ -+ bill@fakesite.com -+ --0000 -+ Content-Disposition: form-data; name="image"; filename="image.jpg" -+ Content-Type: image/jpeg -+ -+ BINARYDATA -+ --0000-- -+ ), -+ ), -+ ), -+}, -+ diff --git a/mod_security-2.9.8-remote-rules-timeout.patch b/mod_security-2.9.8-remote-rules-timeout.patch new file mode 100644 index 0000000000000000000000000000000000000000..2814fdab59e278729989bcb26a299657f20179be --- /dev/null +++ b/mod_security-2.9.8-remote-rules-timeout.patch @@ -0,0 +1,85 @@ +diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c +index 80f8f2b..7912d84 100644 +--- a/apache2/apache2_config.c ++++ b/apache2/apache2_config.c +@@ -2354,6 +2354,24 @@ static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1, + } + + ++static const char *cmd_remote_timeout(cmd_parms *cmd, void *_dcfg, const char *p1) ++{ ++ directory_config *dcfg = (directory_config *)_dcfg; ++ long int timeout; ++ ++ if (dcfg == NULL) return NULL; ++ ++ timeout = strtol(p1, NULL, 10); ++ if ((timeout == LONG_MAX)||(timeout == LONG_MIN)||(timeout < 0)) { ++ return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRemoteTimeout: %s", p1); ++ } ++ ++ remote_rules_timeout = timeout; ++ ++ return NULL; ++} ++ ++ + static const char *cmd_status_engine(cmd_parms *cmd, void *_dcfg, const char *p1) + { + assert(cmd != NULL); +@@ -3667,6 +3685,14 @@ const command_rec module_directives[] = { + "Abort or Warn" + ), + ++ AP_INIT_TAKE1 ( ++ "SecRemoteTimeout", ++ cmd_remote_timeout, ++ NULL, ++ CMD_SCOPE_ANY, ++ "timeout in seconds" ++ ), ++ + + AP_INIT_TAKE1 ( + "SecXmlExternalEntity", +diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c +index 7bb215e..c155495 100644 +--- a/apache2/mod_security2.c ++++ b/apache2/mod_security2.c +@@ -79,6 +79,8 @@ msc_remote_rules_server DSOLOCAL *remote_rules_server = NULL; + #endif + int DSOLOCAL remote_rules_fail_action = REMOTE_RULES_ABORT_ON_FAIL; + char DSOLOCAL *remote_rules_fail_message = NULL; ++unsigned long int DSOLOCAL remote_rules_timeout = NOT_SET; ++ + + int DSOLOCAL status_engine_state = STATUS_ENGINE_DISABLED; + +diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h +index f24bc75..8bcd453 100644 +--- a/apache2/modsecurity.h ++++ b/apache2/modsecurity.h +@@ -150,6 +150,7 @@ extern DSOLOCAL msc_remote_rules_server *remote_rules_server; + #endif + extern DSOLOCAL int remote_rules_fail_action; + extern DSOLOCAL char *remote_rules_fail_message; ++extern DSOLOCAL unsigned long int remote_rules_timeout; + + extern DSOLOCAL int status_engine_state; + +diff --git a/apache2/msc_remote_rules.c b/apache2/msc_remote_rules.c +index 99968f0..b8db13e 100644 +--- a/apache2/msc_remote_rules.c ++++ b/apache2/msc_remote_rules.c +@@ -358,6 +358,11 @@ int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key + /* We want Curl to return error in case there is an HTTP error code */ + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + ++ /* In case we want different timeout than a default one */ ++ if (remote_rules_timeout != NOT_SET){ ++ curl_easy_setopt(curl, CURLOPT_TIMEOUT, remote_rules_timeout); ++ } ++ + res = curl_easy_perform(curl); + + if (res != CURLE_OK) diff --git a/mod_security.conf b/mod_security.conf index 12e87c419715601f12e8406c37ad03022131ad8f..e9fe3ddcee5fb67af7c78b8c8613b17b06d3465e 100644 --- a/mod_security.conf +++ b/mod_security.conf @@ -1,226 +1,56 @@ -# -- Rule engine initialization ---------------------------------------------- - -# Enable ModSecurity, attaching it to every transaction. Use detection -# only to start with, because that minimises the chances of post-installation -# disruption. -# -SecRuleEngine DetectionOnly - - -# -- Request body handling --------------------------------------------------- - -# Allow ModSecurity to access request bodies. If you don't, ModSecurity -# won't be able to see any POST parameters, which opens a large security -# hole for attackers to exploit. -# -SecRequestBodyAccess On - - -# Enable XML request body parser. -# Initiate XML Processor in case of xml content-type -# -SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ - "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" - -# Enable JSON request body parser. -# Initiate JSON Processor in case of JSON content-type; change accordingly -# if your application does not use 'application/json' -# -SecRule REQUEST_HEADERS:Content-Type "application/json" \ - "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" - -# Maximum request body size we will accept for buffering. If you support -# file uploads then the value given on the first line has to be as large -# as the largest file you are willing to accept. The second value refers -# to the size of data, with files excluded. You want to keep that value as -# low as practical. -# -SecRequestBodyLimit 13107200 -SecRequestBodyNoFilesLimit 131072 - -# Store up to 128 KB of request body data in memory. When the multipart -# parser reachers this limit, it will start using your hard disk for -# storage. That is slow, but unavoidable. -# -SecRequestBodyInMemoryLimit 131072 - -# What do do if the request body size is above our configured limit. -# Keep in mind that this setting will automatically be set to ProcessPartial -# when SecRuleEngine is set to DetectionOnly mode in order to minimize -# disruptions when initially deploying ModSecurity. -# -SecRequestBodyLimitAction Reject - -# Verify that we've correctly processed the request body. -# As a rule of thumb, when failing to process a request body -# you should reject the request (when deployed in blocking mode) -# or log a high-severity alert (when deployed in detection-only mode). -# -SecRule REQBODY_ERROR "!@eq 0" \ -"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" - -# By default be strict with what we accept in the multipart/form-data -# request body. If the rule below proves to be too strict for your -# environment consider changing it to detection-only. You are encouraged -# _not_ to remove it altogether. -# -SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ -"id:'200003',phase:2,t:none,log,deny,status:400, \ -msg:'Multipart request body failed strict validation: \ -PE %{REQBODY_PROCESSOR_ERROR}, \ -BQ %{MULTIPART_BOUNDARY_QUOTED}, \ -BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ -DB %{MULTIPART_DATA_BEFORE}, \ -DA %{MULTIPART_DATA_AFTER}, \ -HF %{MULTIPART_HEADER_FOLDING}, \ -LF %{MULTIPART_LF_LINE}, \ -SM %{MULTIPART_MISSING_SEMICOLON}, \ -IQ %{MULTIPART_INVALID_QUOTING}, \ -IP %{MULTIPART_INVALID_PART}, \ -IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ -FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" - -# Did we see anything that might be a boundary? -# -SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ -"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" - -# PCRE Tuning -# We want to avoid a potential RegEx DoS condition -# -SecPcreMatchLimit 1000 -SecPcreMatchLimitRecursion 1000 - -# Some internal errors will set flags in TX and we will need to look for these. -# All of these are prefixed with "MSC_". The following flags currently exist: -# -# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. -# -SecRule TX:/^MSC_/ "!@streq 0" \ - "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" - - -# -- Response body handling -------------------------------------------------- - -# Allow ModSecurity to access response bodies. -# You should have this directive enabled in order to identify errors -# and data leakage issues. -# -# Do keep in mind that enabling this directive does increases both -# memory consumption and response latency. -# -SecResponseBodyAccess On - -# Which response MIME types do you want to inspect? You should adjust the -# configuration below to catch documents but avoid static files -# (e.g., images and archives). -# -SecResponseBodyMimeType text/plain text/html text/xml - -# Buffer response bodies of up to 512 KB in length. -SecResponseBodyLimit 524288 - -# What happens when we encounter a response body larger than the configured -# limit? By default, we process what we have and let the rest through. -# That's somewhat less secure, but does not break any legitimate pages. -# -SecResponseBodyLimitAction ProcessPartial - - -# -- Filesystem configuration ------------------------------------------------ - -# The location where ModSecurity stores temporary files (for example, when -# it needs to handle a file upload that is larger than the configured limit). -# -# This default setting is chosen due to all systems have /tmp available however, -# this is less than ideal. It is recommended that you specify a location that's private. -# -SecTmpDir /tmp/ - -# The location where ModSecurity will keep its persistent data. This default setting -# is chosen due to all systems have /tmp available however, it -# too should be updated to a place that other users can't access. -# -SecDataDir /tmp/ - - -# -- File uploads handling configuration ------------------------------------- - -# The location where ModSecurity stores intercepted uploaded files. This -# location must be private to ModSecurity. You don't want other users on -# the server to access the files, do you? -# -#SecUploadDir /opt/modsecurity/var/upload/ - -# By default, only keep the files that were determined to be unusual -# in some way (by an external inspection script). For this to work you -# will also need at least one file inspection rule. -# -#SecUploadKeepFiles RelevantOnly - -# Uploaded files are by default created with permissions that do not allow -# any other user to access them. You may need to relax that if you want to -# interface ModSecurity to an external program (e.g., an anti-virus). -# -#SecUploadFileMode 0600 - - -# -- Debug log configuration ------------------------------------------------- - -# The default debug log configuration is to duplicate the error, warning -# and notice messages from the error log. -# -#SecDebugLog /opt/modsecurity/var/log/debug.log -#SecDebugLogLevel 3 - - -# -- Audit log configuration ------------------------------------------------- - -# Log the transactions that are marked by a rule, as well as those that -# trigger a server error (determined by a 5xx or 4xx, excluding 404, -# level response status codes). -# -SecAuditEngine RelevantOnly -SecAuditLogRelevantStatus "^(?:5|4(?!04))" - -# Log everything we know about a transaction. -SecAuditLogParts ABIJDEFHZ - -# Use a single file for logging. This is much easier to look at, but -# assumes that you will use the audit log only ocassionally. -# -SecAuditLogType Serial -SecAuditLog /var/log/modsec_audit.log - -# Specify the path for concurrent audit logging. -#SecAuditLogStorageDir /opt/modsecurity/var/audit/ - - -# -- Miscellaneous ----------------------------------------------------------- - -# Use the most commonly used application/x-www-form-urlencoded parameter -# separator. There's probably only one application somewhere that uses -# something else so don't expect to change this value. -# -SecArgumentSeparator & - -# Settle on version 0 (zero) cookies, as that is what most applications -# use. Using an incorrect cookie version may open your installation to -# evasion attacks (against the rules that examine named cookies). -# -SecCookieFormat 0 - -# Specify your Unicode Code Point. -# This mapping is used by the t:urlDecodeUni transformation function -# to properly map encoded data to your language. Properly setting -# these directives helps to reduce false positives and negatives. -# -#SecUnicodeMapFile unicode.mapping 20127 - -# Improve the quality of ModSecurity by sharing information about your -# current ModSecurity version and dependencies versions. -# The following information will be shared: ModSecurity version, -# Web Server version, APR version, PCRE version, Lua version, Libxml2 -# version, Anonymous unique id for host. -SecStatusEngine On - + + # Default recommended configuration + SecRuleEngine On + SecRequestBodyAccess On + SecRule REQUEST_HEADERS:Content-Type "text/xml" \ + "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" + SecRequestBodyLimit 13107200 + SecRequestBodyNoFilesLimit 131072 + SecRequestBodyInMemoryLimit 131072 + SecRequestBodyLimitAction Reject + SecRule REQBODY_ERROR "!@eq 0" \ + "id:'200001', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" + SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ + "id:'200002',phase:2,t:none,log,deny,status:400,msg:'Multipart request body \ + failed strict validation: \ + PE %{REQBODY_PROCESSOR_ERROR}, \ + BQ %{MULTIPART_BOUNDARY_QUOTED}, \ + BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ + DB %{MULTIPART_DATA_BEFORE}, \ + DA %{MULTIPART_DATA_AFTER}, \ + HF %{MULTIPART_HEADER_FOLDING}, \ + LF %{MULTIPART_LF_LINE}, \ + SM %{MULTIPART_MISSING_SEMICOLON}, \ + IQ %{MULTIPART_INVALID_QUOTING}, \ + IP %{MULTIPART_INVALID_PART}, \ + IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ + FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + + SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ + "id:'200003',phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'" + + SecPcreMatchLimit 1000 + SecPcreMatchLimitRecursion 1000 + + SecRule TX:/^MSC_/ "!@streq 0" \ + "id:'200004',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" + + SecResponseBodyAccess Off + SecDebugLog /var/log/httpd/modsec_debug.log + SecDebugLogLevel 0 + SecAuditEngine RelevantOnly + SecAuditLogRelevantStatus "^(?:5|4(?!04))" + SecAuditLogParts ABIJDEFHZ + SecAuditLogType Serial + SecAuditLog /var/log/httpd/modsec_audit.log + SecArgumentSeparator & + SecCookieFormat 0 + SecTmpDir /var/lib/mod_security + SecDataDir /var/lib/mod_security + + # ModSecurity Core Rules Set and Local configuration + Include modsecurity.d/*.conf + Include modsecurity.d/activated_rules/*.conf + Include modsecurity.d/local_rules/*.conf + + diff --git a/mod_security.spec b/mod_security.spec index abe32da323aea1058a4b36258078269e1348f548..36b3dc3c5fdfc2d88c427d65737abc4b17dc27d2 100644 --- a/mod_security.spec +++ b/mod_security.spec @@ -6,21 +6,23 @@ %global mod_audit_log_collector 0 Name: mod_security -Version: 2.9.5 -Release: 3 +Version: 2.9.9 +Release: 1 Summary: Security module for the Apache HTTP Server -License: ASL 2.0 -URL: http://www.modsecurity.org/ -Source: https://github.com/SpiderLabs/ModSecurity/releases/download/v%{version}/modsecurity-%{version}.tar.gz +License: Apache-2.0 +URL: https://www.modsecurity.org/ +Source: https://github.com/owasp-modsecurity/ModSecurity/releases/download/v%{version}/modsecurity-v%{version}.tar.gz Source1: mod_security.conf Source2: 10-mod_security.conf Source3: modsecurity_localrules.conf -# https://github.com/SpiderLabs/ModSecurity/commit/51a30d7b406af95c4143560d9753cf0b6d2151f5 -Patch0: CVE-2022-48279.patch +Patch0001: modsecurity-2.9.3-apulibs.patch +Patch0002: mod_security-2.9.8-remote-rules-timeout.patch + Requires: httpd httpd-mmn = %{_httpd_mmn} -BuildRequires: gcc make perl-generators httpd-devel yajl yajl-devel -BuildRequires: pkgconfig(lua) pkgconfig(libcurl) pkgconfig(libxml-2.0) pkgconfig(libpcre) +BuildRequires: gcc make perl-generators httpd-devel yajl yajl-devel pcre2-devel +BuildRequires: pkgconfig(lua) pkgconfig(libcurl) pkgconfig(libxml-2.0) +BuildRequires: autoconf automake libtool %description This software is also called Modsec,it is an open-source web application firewall. @@ -38,13 +40,20 @@ This package collects mod_security audit log. %endif %prep -%autosetup -p1 -n modsecurity-%{version} +%autosetup -p1 -n modsecurity-v%{version} %build -%configure --with-yajl --with-apxs=%{_httpd_apxs} --enable-pcre-match-limit-recursion=1000000 --enable-pcre-match-limit=1000000 +./autogen.sh +%configure --enable-pcre-match-limit=1000000 \ + --enable-pcre-match-limit-recursion=1000000 \ + --with-apxs=%{_httpd_apxs} \ + --with-yajl \ + --with-pcre2 \ + --disable-static + sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool -make %{_smp_mflags} +%make_build %install install -d %{buildroot}%{_bindir} @@ -55,14 +64,14 @@ install -d %{buildroot}%{_sysconfdir}/httpd/modsecurity.d/ install -d %{buildroot}%{_sysconfdir}/httpd/modsecurity.d/local_rules install -d %{buildroot}%{_sysconfdir}/httpd/modsecurity.d/activated_rules install -m 700 -d $RPM_BUILD_ROOT%{_localstatedir}/lib/%{name} -install -Dp -m0644 %{SOURCE3} %{buildroot}%{_sysconfdir}/httpd/modsecurity.d/local_rules/ +install -Dp -m0644 %{S:3} %{buildroot}%{_sysconfdir}/httpd/modsecurity.d/local_rules/ %if "%{_httpd_modconfdir}" != "%{_httpd_confdir}" -install -Dp -m0644 %{SOURCE1} %{buildroot}%{_httpd_confdir}/mod_security.conf +install -Dp -m0644 %{S:1} %{buildroot}%{_httpd_confdir}/mod_security.conf sed -i 's/Include/IncludeOptional/' %{buildroot}%{_httpd_confdir}/mod_security.conf -install -Dp -m0644 %{SOURCE2} %{buildroot}%{_httpd_modconfdir}/10-mod_security.conf +install -Dp -m0644 %{S:2} %{buildroot}%{_httpd_modconfdir}/10-mod_security.conf %else install -d -m0755 %{buildroot}%{_httpd_confdir} -cat %{SOURCE2} %{SOURCE1} > %{buildroot}%{_httpd_confdir}/mod_security.conf +cat %{S:2} %{S:1} > %{buildroot}%{_httpd_confdir}/mod_security.conf %endif %if %mod_audit_log_collector @@ -74,7 +83,8 @@ install -m0755 mlogc/mlogc-batch-load.pl %{buildroot}%{_bindir}/mlogc-batch-load %endif %files -%doc README.* NOTICE LICENSE CHANGES +%license LICENSE +%doc README.* NOTICE CHANGES %{_httpd_moddir}/mod_security2.so %config(noreplace) %{_httpd_confdir}/*.conf %if "%{_httpd_modconfdir}" != "%{_httpd_confdir}" @@ -97,6 +107,16 @@ install -m0755 mlogc/mlogc-batch-load.pl %{buildroot}%{_bindir}/mlogc-batch-load %endif %changelog +* Mon May 26 2025 wangkai <13474090681@163.com> - 2.9.9-1 +- Update to 2.9.9 for fix CVE-2025-47947 + +* Mon Sep 09 2024 Funda Wang - 2.9.8-1 +- update to 2.9.8 + +* Mon Jul 17 2023 chenchen - 2.9.7-1 +- Upgrade to version 2.9.7 +- Fix CVE-2022-39956 + * Mon May 06 2024 yaoxin - 2.9.5-3 - Fix HTTP service startup failure diff --git a/mod_security.yaml b/mod_security.yaml new file mode 100644 index 0000000000000000000000000000000000000000..69478be180b0c434ea41cfdb22315efe1727dc85 --- /dev/null +++ b/mod_security.yaml @@ -0,0 +1,4 @@ +version_control: github +src_repo: owasp-modsecurity/ModSecurity +tag_prefix: ^v +separator: . diff --git a/modsecurity-2.9.3-apulibs.patch b/modsecurity-2.9.3-apulibs.patch new file mode 100644 index 0000000000000000000000000000000000000000..bde44bb3c697ea447408c66b785c537d3a8e4d7b --- /dev/null +++ b/modsecurity-2.9.3-apulibs.patch @@ -0,0 +1,14 @@ + +Strip redundant APR-util dependent libraries, it is sufficient to link against -laprutil-1. + +--- modsecurity-2.9.3/build/find_apu.m4.apulibs ++++ modsecurity-2.9.3/build/find_apu.m4 +@@ -59,7 +59,7 @@ + APU_CFLAGS="`${APU_CONFIG} --includes`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu CFLAGS: $APU_CFLAGS); fi + APU_LDFLAGS="`${APU_CONFIG} --ldflags`" +- APU_LDFLAGS="$APU_LDFLAGS `${APU_CONFIG} --libs`" ++ APU_LDFLAGS="$APU_LDFLAGS `${APU_CONFIG} --avoid-ldap --avoid-dbm --libs`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDFLAGS: $APU_LDFLAGS); fi + APU_LDADD="`${APU_CONFIG} --link-libtool`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apu LDADD: $APU_LDADD); fi diff --git a/modsecurity-2.9.5.tar.gz b/modsecurity-v2.9.9.tar.gz similarity index 57% rename from modsecurity-2.9.5.tar.gz rename to modsecurity-v2.9.9.tar.gz index cbbf85bfd7b30fa238ae6cb1d2d59af9df977379..f3649b8e3b452c031dd1d8e826cc1d896eef1e00 100644 Binary files a/modsecurity-2.9.5.tar.gz and b/modsecurity-v2.9.9.tar.gz differ diff --git a/modsecurity_localrules.conf b/modsecurity_localrules.conf index 13935cd13185526fbf89d4e7bae60c0da363766c..983d7df3c459efa3b0c713f01e9ee7cee12f6419 100644 --- a/modsecurity_localrules.conf +++ b/modsecurity_localrules.conf @@ -1,3 +1,5 @@ +# User defined rules and settings . +# # You can use this file/directory to drop your local rules or # to remove some rules provided by mod_security_crs package with SecRuleRemoveById #