diff options
| author | Natalia <124304+nessita@users.noreply.github.com> | 2026-03-05 14:41:44 -0300 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-04-07 07:33:47 -0400 |
| commit | 0b467893bdde69a2d23034338e76021a1e4f4322 (patch) | |
| tree | c1afd7bfbda68d3e9bacce5b475af4d906f5659a /django/http/multipartparser.py | |
| parent | 397c22048244db2cd4bb78f570e6c72a3967bf36 (diff) | |
[5.2.x] Fixed CVE-2026-33033 -- Mitigated potential DoS in MultiPartParser.
When a multipart file part used `Content-Transfer-Encoding: base64` and
the non-whitespace base64 bytes did not align to a multiple of 4 within
a chunk, the parser entered a loop calling `field_stream.read(1-3)` once
per whitespace byte. Each such call fetched the entire internal buffer,
sliced off 1-3 bytes, and pushed the remainder back via unget(), doing
an O(n) memory copy per call. A 2.5 MB payload of mostly whitespace
produced CPU amplification relative to a normal upload of the same size.
The alignment loop now reads `self._chunk_size` bytes at a time, and
accumulates stripped parts in a list joined once at the end.
Thanks to Seokchan Yoon for the report and the fixing patch.
Backport of 7e9885f99cee771b51692fadc5592bdbf19641aa from main.
Diffstat (limited to 'django/http/multipartparser.py')
| -rw-r--r-- | django/http/multipartparser.py | 15 |
1 files changed, 9 insertions, 6 deletions
diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index 58b8546be7..5edf024b31 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -304,15 +304,18 @@ class MultiPartParser: # We should always decode base64 chunks by # multiple of 4, ignoring whitespace. - stripped_chunk = b"".join(chunk.split()) + stripped_parts = [b"".join(chunk.split())] + stripped_length = len(stripped_parts[0]) - remaining = len(stripped_chunk) % 4 - while remaining != 0: - over_chunk = field_stream.read(4 - remaining) + while stripped_length % 4 != 0: + over_chunk = field_stream.read(self._chunk_size) if not over_chunk: break - stripped_chunk += b"".join(over_chunk.split()) - remaining = len(stripped_chunk) % 4 + over_stripped = b"".join(over_chunk.split()) + stripped_parts.append(over_stripped) + stripped_length += len(over_stripped) + + stripped_chunk = b"".join(stripped_parts) try: chunk = base64.b64decode(stripped_chunk) |
