diff mbox series

[meta-python,kirkstone,1/1] python3-werkzeug: Fix CVE-2024-49767

Message ID 20241227083332.1928449-1-soumya.sambu@windriver.com
State New
Headers show
Series [meta-python,kirkstone,1/1] python3-werkzeug: Fix CVE-2024-49767 | expand

Commit Message

ssambu Dec. 27, 2024, 8:33 a.m. UTC
From: Soumya Sambu <soumya.sambu@windriver.com>

Werkzeug is a Web Server Gateway Interface web application library. Applications
using `werkzeug.formparser.MultiPartParser` corresponding to a version of Werkzeug
prior to 3.0.6 to parse `multipart/form-data` requests (e.g. all flask applications)
are vulnerable to a relatively simple but effective resource exhaustion (denial of
service) attack. A specifically crafted form submission request can cause the parser
to allocate and block 3 to 8 times the upload size in main memory. There is no upper
limit; a single upload at 1 Gbit/s can exhaust 32 GB of RAM in less than 60 seconds.
Werkzeug version 3.0.6 fixes this issue.

Reference:
https://nvd.nist.gov/vuln/detail/CVE-2024-49767

Upstream-patch:
https://github.com/pallets/werkzeug/commit/8760275afb72bd10b57d92cb4d52abf759b2f3a7

Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
---
 .../python3-werkzeug/CVE-2024-49767.patch     | 87 +++++++++++++++++++
 .../python/python3-werkzeug_2.1.1.bb          |  3 +-
 2 files changed, 89 insertions(+), 1 deletion(-)
 create mode 100644 meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-49767.patch
diff mbox series

Patch

diff --git a/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-49767.patch b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-49767.patch
new file mode 100644
index 0000000000..279c6e522d
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-49767.patch
@@ -0,0 +1,87 @@ 
+From 8760275afb72bd10b57d92cb4d52abf759b2f3a7 Mon Sep 17 00:00:00 2001
+From: David Lord <davidism@gmail.com>
+Date: Fri, 25 Oct 2024 06:46:50 -0700
+Subject: [PATCH] apply max_form_memory_size another level up in the parser
+
+CVE: CVE-2024-49767
+
+Upstream-Status: Backport [https://github.com/pallets/werkzeug/commit/8760275afb72bd10b57d92cb4d52abf759b2f3a7]
+
+Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
+---
+ src/werkzeug/formparser.py       | 11 +++++++++++
+ src/werkzeug/sansio/multipart.py |  2 ++
+ tests/test_formparser.py         | 12 ++++++++++++
+ 3 files changed, 25 insertions(+)
+
+diff --git a/src/werkzeug/formparser.py b/src/werkzeug/formparser.py
+index bebb2fc..b82af82 100644
+--- a/src/werkzeug/formparser.py
++++ b/src/werkzeug/formparser.py
+@@ -405,6 +405,7 @@ class MultiPartParser:
+     def parse(
+         self, stream: t.IO[bytes], boundary: bytes, content_length: t.Optional[int]
+     ) -> t.Tuple[MultiDict, MultiDict]:
++        field_size: int | None = None
+         container: t.Union[t.IO[bytes], t.List[bytes]]
+         _write: t.Callable[[bytes], t.Any]
+
+@@ -431,13 +432,23 @@ class MultiPartParser:
+             while not isinstance(event, (Epilogue, NeedData)):
+                 if isinstance(event, Field):
+                     current_part = event
++                    field_size = 0
+                     container = []
+                     _write = container.append
+                 elif isinstance(event, File):
+                     current_part = event
++                    field_size = None
+                     container = self.start_file_streaming(event, content_length)
+                     _write = container.write
+                 elif isinstance(event, Data):
++                    if self.max_form_memory_size is not None and field_size is not None:
++                        # Ensure that accumulated data events do not exceed limit.
++                        # Also checked within single event in MultipartDecoder.
++                        field_size += len(event.data)
++
++                        if field_size > self.max_form_memory_size:
++                            raise RequestEntityTooLarge()
++
+                     _write(event.data)
+                     if not event.more_data:
+                         if isinstance(current_part, Field):
+diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py
+index e7d742b..a91fedd 100644
+--- a/src/werkzeug/sansio/multipart.py
++++ b/src/werkzeug/sansio/multipart.py
+@@ -137,6 +137,8 @@ class MultipartDecoder:
+             self.max_form_memory_size is not None
+             and len(self.buffer) + len(data) > self.max_form_memory_size
+         ):
++            # Ensure that data within single event does not exceed limit.
++            # Also checked across accumulated events in MultiPartParser.
+             raise RequestEntityTooLarge()
+         else:
+             self.buffer.extend(data)
+diff --git a/tests/test_formparser.py b/tests/test_formparser.py
+index 834324f..1a178dd 100644
+--- a/tests/test_formparser.py
++++ b/tests/test_formparser.py
+@@ -459,3 +459,15 @@ class TestMultiPartParser:
+         )
+         assert request.files["rfc2231"].filename == "a b c d e f.txt"
+         assert request.files["rfc2231"].read() == b"file contents"
++
++
++def test_multipart_max_form_memory_size() -> None:
++    """max_form_memory_size is tracked across multiple data events."""
++    data = b"--bound\r\nContent-Disposition: form-field; name=a\r\n\r\n"
++    data += b"a" * 15 + b"\r\n--bound--"
++    # The buffer size is less than the max size, so multiple data events will be
++    # returned. The field size is greater than the max.
++    parser = formparser.MultiPartParser(max_form_memory_size=10, buffer_size=5)
++
++    with pytest.raises(RequestEntityTooLarge):
++        parser.parse(io.BytesIO(data), b"bound", None)
+--
+2.40.0
diff --git a/meta-python/recipes-devtools/python/python3-werkzeug_2.1.1.bb b/meta-python/recipes-devtools/python/python3-werkzeug_2.1.1.bb
index 12f6dff17d..5529686aac 100644
--- a/meta-python/recipes-devtools/python/python3-werkzeug_2.1.1.bb
+++ b/meta-python/recipes-devtools/python/python3-werkzeug_2.1.1.bb
@@ -15,7 +15,8 @@  PYPI_PACKAGE = "Werkzeug"
 SRC_URI += "file://CVE-2023-25577.patch \
             file://CVE-2023-23934.patch \
             file://CVE-2024-34069-0001.patch \
-            file://CVE-2024-34069-0002.patch"
+            file://CVE-2024-34069-0002.patch \
+            file://CVE-2024-49767.patch"
 
 SRC_URI[sha256sum] = "f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74"