From 164de0f389660f2ffd8b10a05341919b72cc8f8b Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 12 Apr 2026 23:20:57 +0200 Subject: [PATCH 1/3] fix: buffer overflow in multipart body proc --- src/request_body_processor/multipart.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index 3ae591671e..91503f4f4b 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -362,7 +362,7 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) { const char* start_of_filename = p; while ((*p != '\0') && (*p != ';')) { if (*p == '%') { - if ((*(p+1) == '\0') || (!isxdigit(*(p+1))) || (!isxdigit(*(p+2)))) { + if ((*(p+1) == '\0') || (!isxdigit(*(p+1))) || (*(p+2) == '\0') || (!isxdigit(*(p+2)))) { return -18; } p += 3; @@ -415,7 +415,9 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) { value.append((p++), 1); } - p++; /* go over the quote at the end */ + if (*p == quote) { + p++; /* go over the quote at the end */ + } } else { /* not quoted */ From b9fb2131ae19c7bdb36b147c470188b2e930d9ca Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 12 Apr 2026 23:54:05 +0200 Subject: [PATCH 2/3] cast argument as unsigned --- src/request_body_processor/multipart.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index 91503f4f4b..6315c0eb10 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -362,11 +362,11 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) { const char* start_of_filename = p; while ((*p != '\0') && (*p != ';')) { if (*p == '%') { - if ((*(p+1) == '\0') || (!isxdigit(*(p+1))) || (*(p+2) == '\0') || (!isxdigit(*(p+2)))) { + if ((*(p+1) == '\0') || (!isxdigit(*(p+1))) || (*(p+2) == '\0') || (!isxdigit(static_cast(*(p+2))))) { return -18; } p += 3; - } else if (isalnum(*p) || strchr(attr_char_special, *p)) { + } else if (isalnum(static_cast(*p)) || strchr(attr_char_special, *p)) { p++; } else { return -19; From f9248f903beae118d10f074c6dec84fae1cb5154 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 13 Apr 2026 14:59:24 +0200 Subject: [PATCH 3/3] Check if filename arg is quoted and it has the trailing quote --- src/request_body_processor/multipart.cc | 3 ++ .../request-body-parser-multipart.json | 51 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index 6315c0eb10..0b35672bc9 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -417,6 +417,9 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) { if (*p == quote) { p++; /* go over the quote at the end */ + } else { + m_flag_invalid_quoting = 1; + return -15; /* closing quote not found */ } } else { diff --git a/test/test-cases/regression/request-body-parser-multipart.json b/test/test-cases/regression/request-body-parser-multipart.json index 72cc3a1438..84e61c1e0d 100644 --- a/test/test-cases/regression/request-body-parser-multipart.json +++ b/test/test-cases/regression/request-body-parser-multipart.json @@ -3417,5 +3417,56 @@ "SecruleEngine On", "SecRule REQBODY_PROCESSOR_ERROR \"@eq 1\" \"phase:2,deny,status:403,id:500077\"" ] + }, + { + "enabled": 1, + "version_min": 300000, + "title": "multipart parser (invalid part header - missing trailing quote)", + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "145", + "Content-Type": "multipart/form-data; boundary=a", + "Expect": "100-continue" + }, + "uri": "/", + "method": "POST", + "body": [ + "--a\r\n", + "Content-Disposition: form-data; name=\"file\"; filename=\"1.jsp\r\n", + "\r\n", + "Some content\r\n", + "--a--\r\n" + ] + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "expected": { + "debug_log": "Multipart: Invalid Content-Disposition header \\(-15\\): form-data; name=\"file\"; filename=\"1.jsp", + "http_code": 403 + }, + "rules": [ + "SecruleEngine On", + "SecRule REQBODY_PROCESSOR_ERROR \"@eq 1\" \"phase:2,deny,status:403,id:500077\"" + ] } ]