Skip to content

Commit ec13b20

Browse files
committed
Fix CWE-190 - DoS could happen with a specially crafted boundary parameter in a multipart request
1 parent a87fcdb commit ec13b20

3 files changed

Lines changed: 27 additions & 4 deletions

File tree

src/ESPAsyncWebServer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ class AsyncWebServerRequest {
465465
std::unordered_map<const char *, String, std::hash<const char *>, std::equal_to<const char *>> _attributes;
466466

467467
uint8_t _multiParseState;
468-
uint8_t _boundaryPosition;
468+
size_t _boundaryPosition;
469469
size_t _itemStartIndex;
470470
size_t _itemSize;
471471
String _itemName;

src/WebRequest.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,30 @@ bool AsyncWebServerRequest::_parseReqHeader() {
474474
} else if (name.equalsIgnoreCase(T_Content_Type)) {
475475
_contentType = value.substring(0, value.indexOf(';'));
476476
if (value.startsWith(T_MULTIPART_)) {
477-
_boundary = value.substring(value.indexOf('=') + 1);
477+
String lowcase(value);
478+
lowcase.toLowerCase();
479+
int bpos = lowcase.indexOf(T_BOUNDARY);
480+
if (bpos < 0) {
481+
async_ws_log_d("Missing multipart boundary parameter, aborting");
482+
_parseState = PARSE_REQ_FAIL;
483+
abort();
484+
return true;
485+
}
486+
_boundary = value.substring(bpos + T_BOUNDARY_LEN);
487+
int semi = _boundary.indexOf(';');
488+
if (semi >= 0) {
489+
_boundary = _boundary.substring(0, semi);
490+
}
491+
_boundary.trim();
478492
_boundary.replace(String('"'), String());
493+
// RFC 2046 §5.1 limits boundary strings to 70 characters.
494+
// Reject invalid boundaries to prevent integer overflow in the parser.
495+
if (_boundary.length() == 0 || _boundary.length() > 70) {
496+
async_ws_log_d("Invalid multipart boundary length (%u), aborting", _boundary.length());
497+
_parseState = PARSE_REQ_FAIL;
498+
abort();
499+
return true;
500+
}
479501
_isMultipart = true;
480502
}
481503
} else if (name.equalsIgnoreCase(T_Content_Length) || name.equalsIgnoreCase(T_X_Expected_Entity_Length)) {
@@ -743,8 +765,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
743765
itemWriteByte('\n');
744766
itemWriteByte('-');
745767
itemWriteByte('-');
746-
uint8_t i;
747-
for (i = 0; i < _boundaryPosition; i++) {
768+
for (size_t i = 0; i < _boundaryPosition; i++) {
748769
itemWriteByte(_boundary.c_str()[i]);
749770
}
750771
_parseMultipartPostByte(data, last);

src/literals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ static constexpr const char T_username[] = "username";
108108
static constexpr const char T_WS[] = "websocket";
109109
static constexpr const char T_WWW_AUTH[] = "WWW-Authenticate";
110110
static constexpr const char T_X_Expected_Entity_Length[] = "X-Expected-Entity-Length";
111+
static constexpr const char T_BOUNDARY[] = "boundary=";
112+
static constexpr size_t T_BOUNDARY_LEN = sizeof(T_BOUNDARY) - 1;
111113

112114
// HTTP Methods
113115
static constexpr const char T_GET[] = "GET";

0 commit comments

Comments
 (0)