Skip to content

Commit 151074a

Browse files
authored
Merge pull request #3646 from owasp-modsecurity/v2/master2915
V2/master2915
2 parents 830c5d9 + 625dd43 commit 151074a

26 files changed

Lines changed: 3511 additions & 60 deletions

‎CHANGES‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
29 Sep 2026 - 2.9.15
2+
--------------------
3+
4+
* fix: t:htmlEntityDecode does not decode all ASCII named HTML entities
5+
[PR from private repo - @marcstern, @fzipi, @airween; fixed GHSA-cxqf-vgrr-xxrv]
6+
* fix: use secure value for host verification in case of remote rules download
7+
[PR from private repo - @amitu314, @airween; fixed GHSA-2vqc-36qp-ccmw]
8+
* fix: add guard to check XML parser context pointer before use
9+
[PR from private repo - Tobias Klein (www.trapkit.de), @airween; fixed GHSA-jx3r-phvx-2jmj]
10+
* fix: t:removeComments behavior in case of adjacent comments
11+
[PR from private repo - @HEXER365, @airween; fixed GHSA-qrch-pjfr-9g47]
12+
* fix: t:base64DecodeExt does not handle '-' and '_' characters (URL-safe alphabet)
13+
[PR from private repo - @fzipi, @airween; fixed GHSA-4j47-8qcr-jf59]
14+
* fix: handle 'filename*' and duplicated 'filename' parameters in multipart Content-Disposition header;
15+
add new variables MULTIPART_DUPLICATE_PART_HEADER, MULTIPART_FILENAME_CHARSET, MULTIPART_FILENAME_LANGUAGE
16+
[PR from private repo - @hnakamur, @fzipi, @theseion, @airween; fixed GHSA-5pww-8rfg-9crf]
17+
* fix(iis): correct InstallModule32/64 CustomAction ID naming swap
18+
[Issue #3603 - @fzipi]
19+
120
02 Jul 2026 - 2.9.14
221
--------------------
322

‎apache2/modsecurity.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,6 @@ struct modsec_rec {
392392
char *msc_full_request_buffer;
393393
int msc_full_request_length;
394394

395-
char *multipart_filename;
396-
char *multipart_name;
397395
multipart_data *mpd; /* MULTIPART processor data structure */
398396

399397
xml_data *xml; /* XML processor data structure */

‎apache2/msc_multipart.c‎

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "msc_util.h"
2121
#include "msc_parsers.h"
2222

23+
static const char* mime_charset_special = "!#$%&+-^_`{}~";
24+
static const char* attr_char_special = "!#$&+-.^_`~";
25+
2326
void validate_quotes(modsec_rec *msr, char *data, char quote) {
2427
assert(msr != NULL);
2528
int i, len;
@@ -130,7 +133,53 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
130133
*/
131134

132135
char quote = '\0';
133-
if ((*p == '"') || (*p == '\'')) {
136+
if (strcmp(name, "filename*") == 0) {
137+
/* filename*=charset'[optional-language]'filename */
138+
/* Read beyond the charset and the optional language*/
139+
const char* start_of_charset = p;
140+
while ((*p != '\0') && (isalnum(*p) || (strchr(mime_charset_special, *p)))) {
141+
p++;
142+
}
143+
if ((*p != '\'') || (p == start_of_charset)) {
144+
return -16; // Must be at least one legit char before ' for start of language
145+
}
146+
msr->mpd->mpp->filename_charset = apr_pstrmemdup(msr->mp, start_of_charset, (p - start_of_charset));
147+
if (msr->txcfg->debuglog_level >= 9) {
148+
msr_log(msr, 9, "Multipart: Content-Disposition filename* charset: %s",
149+
log_escape_nq(msr->mp, msr->mpd->mpp->filename_charset));
150+
}
151+
p++;
152+
const char* start_of_language = p;
153+
while ((*p != '\0') && (*p != '\'')) {
154+
p++;
155+
}
156+
if (*p != '\'') {
157+
msr->mpd->flag_invalid_quoting = 1;
158+
return -17; // Single quote for end-of-language not found
159+
}
160+
msr->mpd->mpp->filename_language = apr_pstrmemdup(msr->mp, start_of_language, (p - start_of_language));
161+
if (msr->txcfg->debuglog_level >= 9) {
162+
msr_log(msr, 9, "Multipart: Content-Disposition filename* language: %s",
163+
log_escape_nq(msr->mp, msr->mpd->mpp->filename_language));
164+
}
165+
p++;
166+
167+
/* Now read what should be the actual filename */
168+
const char* start_of_filename = p;
169+
while ((*p != '\0') && (*p != ';')) {
170+
if (*p == '%') {
171+
if ((*(p+1) == '\0') || (*(p+2) == '\0') || (!isxdigit(*(p+1))) || (!isxdigit(*(p+2)))) {
172+
return -18;
173+
}
174+
p += 3;
175+
} else if (isalnum(*p) || strchr(attr_char_special, *p)) {
176+
p++;
177+
} else {
178+
return -19;
179+
}
180+
}
181+
value = apr_pmemdup(msr->mp, start_of_filename, p - start_of_filename);
182+
} else if ((*p == '"') || (*p == '\'')) {
134183
/* quoted */
135184
quote = *p; // remember which quote character was used for the value
136185

@@ -191,9 +240,8 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
191240

192241
validate_quotes(msr, value, quote);
193242

194-
msr->multipart_name = apr_pstrdup(msr->mp, value);
195-
196243
if (msr->mpd->mpp->name != NULL) {
244+
msr->mpd->flag_duplicate_part_header = 1;
197245
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s",
198246
log_escape_nq(msr->mp, value));
199247
return -14;
@@ -205,23 +253,43 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
205253
log_escape_nq(msr->mp, value));
206254
}
207255
}
208-
else
209-
if (strcmp(name, "filename") == 0) {
210-
211-
validate_quotes(msr, value, quote);
212-
213-
msr->multipart_filename = apr_pstrdup(msr->mp, value);
214-
215-
if (msr->mpd->mpp->filename != NULL) {
256+
else if (strcmp(name, "filename") == 0) {
257+
// check if the 'filename' (and not the `filename*`) is already added from the header, if yes then return error
258+
if (msr->mpd->mpp->filename != NULL && strlen(msr->mpd->mpp->filename) != 0) {
259+
msr->mpd->flag_duplicate_part_header = 1;
216260
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s",
217261
log_escape_nq(msr->mp, value));
218262
return -15;
219263
}
220-
msr->mpd->mpp->filename = value;
264+
else {
265+
validate_quotes(msr, value, quote);
266+
267+
msr->mpd->mpp->filename = apr_pstrdup(msr->mp, value);
221268

269+
if (msr->txcfg->debuglog_level >= 9) {
270+
msr_log(msr, 9, "Multipart: Content-Disposition filename: %s",
271+
log_escape_nq(msr->mp, value));
272+
}
273+
}
274+
} else if (strcmp(name, "filename*") == 0) {
275+
// check if the 'filename*' (and not the `filename`) is already added from the header, if yes then return error
276+
if (msr->mpd->mpp->filename_star != NULL && strlen(msr->mpd->mpp->filename_star) != 0) {
277+
msr->mpd->flag_duplicate_part_header = 1;
278+
msr_log(msr, 4,
279+
"Multipart: Warning: Duplicate Content-Disposition filename*: %s.", log_escape_nq(msr->mp, value));
280+
return -15;
281+
}
282+
int invalid_count = 0;
283+
int changed = 0;
284+
char *decoded_value = apr_pstrdup(msr->mp, value);
285+
int decoded_len = urldecode_nonstrict_inplace_ex_plus((unsigned char *)decoded_value, strlen(decoded_value), 0, &invalid_count, &changed);
286+
if (invalid_count > 0) {
287+
msr->mpd->flag_invalid_part = 1;
288+
}
289+
msr->mpd->mpp->filename_star = apr_pstrdup(msr->mp, decoded_value);
222290
if (msr->txcfg->debuglog_level >= 9) {
223-
msr_log(msr, 9, "Multipart: Content-Disposition filename: %s",
224-
log_escape_nq(msr->mp, value));
291+
msr_log(msr, 9, "Multipart: Content-Disposition filename*: %s.",
292+
log_escape_nq(msr->mp, decoded_value));
225293
}
226294
}
227295
else return -11;
@@ -239,7 +307,7 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value)
239307
}
240308
msr->mpd->flag_invalid_quoting = 1;
241309
}
242-
p++;
310+
// p++;
243311
return -12;
244312
}
245313
p++; /* move over the semi-colon */
@@ -315,7 +383,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
315383
* values from the C-D header. We need to check for the case where they
316384
* didn't understand C-D but we did.
317385
*/
318-
if (strstr(header_value, "filename=") == NULL) {
386+
if (strstr(header_value, "filename=") == NULL && strstr(header_value, "filename*=") == NULL) {
319387
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename).");
320388
return -1;
321389
}
@@ -442,6 +510,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
442510

443511
/* error if the name already exists */
444512
if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) {
513+
msr->mpd->flag_duplicate_part_header = 1;
445514
*error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.",
446515
log_escape_nq(msr->mp, header_name));
447516
return -1;

‎apache2/msc_multipart.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct multipart_part {
5252
unsigned int tmp_file_size;
5353
/* files only, filename as supplied by the browser */
5454
char *filename;
55+
char *filename_star;
56+
char *filename_charset;
57+
char *filename_language;
5558

5659
char *last_header_name;
5760
apr_table_t *headers;
@@ -128,6 +131,7 @@ struct multipart_data {
128131
int flag_error;
129132
int flag_data_before;
130133
int flag_data_after;
134+
int flag_duplicate_part_header;
131135
int flag_header_folding;
132136
int flag_boundary_quoted;
133137
int flag_lf_line;

‎apache2/msc_release.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
#define MODSEC_VERSION_MAJOR "2"
4040
#define MODSEC_VERSION_MINOR "9"
41-
#define MODSEC_VERSION_MAINT "14"
41+
#define MODSEC_VERSION_MAINT "15"
4242
#define MODSEC_VERSION_TYPE ""
4343
#define MODSEC_VERSION_RELEASE ""
4444

‎apache2/msc_remote_rules.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key
344344

345345
/* those are the default options, but lets make sure */
346346
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
347-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
347+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
348348

349349
/* send all data to this function */
350350
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, msc_curl_write_memory_cb);

0 commit comments

Comments
 (0)