summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoumya Sambu <soumya.sambu@windriver.com>2024-12-27 08:33:09 +0000
committerArmin Kuster <akuster808@gmail.com>2024-12-31 09:04:05 -0500
commitc3deda05a749f484a68666ad6de03f690f7bc84b (patch)
treeaf9234e43def43894f5d6fd564e012481ba3e4d9
parent50544ce18c26099edb54c9ccf23f5095bfe652e9 (diff)
downloadmeta-openembedded-c3deda05a749f484a68666ad6de03f690f7bc84b.tar.gz
python3-werkzeug: Fix CVE-2024-34069
Werkzeug is a comprehensive WSGI web application library. The debugger in affected versions of Werkzeug can allow an attacker to execute code on a developer's machine under some circumstances. This requires the attacker to get the developer to interact with a domain and subdomain they control, and enter the debugger PIN, but if they are successful it allows access to the debugger even if it is only running on localhost. This also requires the attacker to guess a URL in the developer's application that will trigger the debugger. This vulnerability is fixed in 3.0.3. Reference: https://nvd.nist.gov/vuln/detail/CVE-2024-34069 Upstream-patches: https://github.com/pallets/werkzeug/commit/71b69dfb7df3d912e66bab87fbb1f21f83504967 https://github.com/pallets/werkzeug/commit/890b6b62634fa61224222aee31081c61b054ff01 Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0001.patch149
-rw-r--r--meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0002.patch120
-rw-r--r--meta-python/recipes-devtools/python/python3-werkzeug_2.1.1.bb4
3 files changed, 272 insertions, 1 deletions
diff --git a/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0001.patch b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0001.patch
new file mode 100644
index 0000000000..74b39df3a3
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0001.patch
@@ -0,0 +1,149 @@
1From 71b69dfb7df3d912e66bab87fbb1f21f83504967 Mon Sep 17 00:00:00 2001
2From: David Lord <davidism@gmail.com>
3Date: Thu, 2 May 2024 11:55:52 -0700
4Subject: [PATCH] restrict debugger trusted hosts
5
6Add a list of `trusted_hosts` to the `DebuggedApplication` middleware. It defaults to only allowing `localhost`, `.localhost` subdomains, and `127.0.0.1`. `run_simple(use_debugger=True)` adds its `hostname` argument to the trusted list as well. The middleware can be used directly to further modify the trusted list in less common development scenarios.
7
8The debugger UI uses the full `document.location` instead of only `document.location.pathname`.
9
10Either of these fixes on their own mitigates the reported vulnerability.
11
12CVE: CVE-2024-34069
13
14Upstream-Status: Backport [https://github.com/pallets/werkzeug/commit/71b69dfb7df3d912e66bab87fbb1f21f83504967]
15
16Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
17---
18 docs/debug.rst | 35 +++++++++++++++++++++++----
19 src/werkzeug/debug/__init__.py | 10 ++++++++
20 src/werkzeug/debug/shared/debugger.js | 4 +--
21 src/werkzeug/serving.py | 3 +++
22 4 files changed, 45 insertions(+), 7 deletions(-)
23
24diff --git a/docs/debug.rst b/docs/debug.rst
25index 25a9f0b..d842135 100644
26--- a/docs/debug.rst
27+++ b/docs/debug.rst
28@@ -16,7 +16,8 @@ interactive debug console to execute code in any frame.
29 The debugger allows the execution of arbitrary code which makes it a
30 major security risk. **The debugger must never be used on production
31 machines. We cannot stress this enough. Do not enable the debugger
32- in production.**
33+ in production.** Production means anything that is not development,
34+ and anything that is publicly accessible.
35
36 .. note::
37
38@@ -72,10 +73,9 @@ argument to get a detailed list of all the attributes it has.
39 Debugger PIN
40 ------------
41
42-Starting with Werkzeug 0.11 the debug console is protected by a PIN.
43-This is a security helper to make it less likely for the debugger to be
44-exploited if you forget to disable it when deploying to production. The
45-PIN based authentication is enabled by default.
46+The debug console is protected by a PIN. This is a security helper to make it
47+less likely for the debugger to be exploited if you forget to disable it when
48+deploying to production. The PIN based authentication is enabled by default.
49
50 The first time a console is opened, a dialog will prompt for a PIN that
51 is printed to the command line. The PIN is generated in a stable way
52@@ -92,6 +92,31 @@ intended to make it harder for an attacker to exploit the debugger.
53 Never enable the debugger in production.**
54
55
56+Allowed Hosts
57+-------------
58+
59+The debug console will only be served if the request comes from a trusted host.
60+If a request comes from a browser page that is not served on a trusted URL, a
61+400 error will be returned.
62+
63+By default, ``localhost``, any ``.localhost`` subdomain, and ``127.0.0.1`` are
64+trusted. ``run_simple`` will trust its ``hostname`` argument as well. To change
65+this further, use the debug middleware directly rather than through
66+``use_debugger=True``.
67+
68+.. code-block:: python
69+
70+ if os.environ.get("USE_DEBUGGER") in {"1", "true"}:
71+ app = DebuggedApplication(app, evalex=True)
72+ app.trusted_hosts = [...]
73+
74+ run_simple("localhost", 8080, app)
75+
76+**This feature is not meant to entirely secure the debugger. It is
77+intended to make it harder for an attacker to exploit the debugger.
78+Never enable the debugger in production.**
79+
80+
81 Pasting Errors
82 --------------
83
84diff --git a/src/werkzeug/debug/__init__.py b/src/werkzeug/debug/__init__.py
85index 49001e0..87e68c4 100644
86--- a/src/werkzeug/debug/__init__.py
87+++ b/src/werkzeug/debug/__init__.py
88@@ -290,6 +290,14 @@ class DebuggedApplication:
89 self._pin, self._pin_cookie = pin_cookie # type: ignore
90 return self._pin
91
92+ self.trusted_hosts: list[str] = [".localhost", "127.0.0.1"]
93+ """List of domains to allow requests to the debugger from. A leading dot
94+ allows all subdomains. This only allows ``".localhost"`` domains by
95+ default.
96+
97+ .. versionadded:: 3.0.3
98+ """
99+
100 @pin.setter
101 def pin(self, value: str) -> None:
102 self._pin = value
103@@ -475,6 +483,8 @@ class DebuggedApplication:
104 # form data! Otherwise the application won't have access to that data
105 # any more!
106 request = Request(environ)
107+ request.trusted_hosts = self.trusted_hosts
108+ assert request.host # will raise 400 error if not trusted
109 response = self.debug_application
110 if request.args.get("__debugger__") == "yes":
111 cmd = request.args.get("cmd")
112diff --git a/src/werkzeug/debug/shared/debugger.js b/src/werkzeug/debug/shared/debugger.js
113index 2354f03..bee079f 100644
114--- a/src/werkzeug/debug/shared/debugger.js
115+++ b/src/werkzeug/debug/shared/debugger.js
116@@ -48,7 +48,7 @@ function initPinBox() {
117 btn.disabled = true;
118
119 fetch(
120- `${document.location.pathname}?__debugger__=yes&cmd=pinauth&pin=${pin}&s=${encodedSecret}`
121+ `${document.location}?__debugger__=yes&cmd=pinauth&pin=${pin}&s=${encodedSecret}`
122 )
123 .then((res) => res.json())
124 .then(({auth, exhausted}) => {
125@@ -79,7 +79,7 @@ function promptForPin() {
126 if (!EVALEX_TRUSTED) {
127 const encodedSecret = encodeURIComponent(SECRET);
128 fetch(
129- `${document.location.pathname}?__debugger__=yes&cmd=printpin&s=${encodedSecret}`
130+ `${document.location}?__debugger__=yes&cmd=printpin&s=${encodedSecret}`
131 );
132 const pinPrompt = document.getElementsByClassName("pin-prompt")[0];
133 fadeIn(pinPrompt);
134diff --git a/src/werkzeug/serving.py b/src/werkzeug/serving.py
135index a19d4bd..84b0664 100644
136--- a/src/werkzeug/serving.py
137+++ b/src/werkzeug/serving.py
138@@ -1038,6 +1038,9 @@ def run_simple(
139 from .debug import DebuggedApplication
140
141 application = DebuggedApplication(application, evalex=use_evalex)
142+ # Allow the specified hostname to use the debugger, in addition to
143+ # localhost domains.
144+ application.trusted_hosts.append(hostname)
145
146 if not is_running_from_reloader():
147 s = prepare_socket(hostname, port)
148--
1492.40.0
diff --git a/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0002.patch b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0002.patch
new file mode 100644
index 0000000000..37d5ba47c7
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-werkzeug/CVE-2024-34069-0002.patch
@@ -0,0 +1,120 @@
1From 890b6b62634fa61224222aee31081c61b054ff01 Mon Sep 17 00:00:00 2001
2From: David Lord <davidism@gmail.com>
3Date: Fri, 3 May 2024 14:49:43 -0700
4Subject: [PATCH] only require trusted host for evalex
5
6CVE: CVE-2024-34069
7
8Upstream-Status: Backport [https://github.com/pallets/werkzeug/commit/890b6b62634fa61224222aee31081c61b054ff01]
9
10Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
11---
12 src/werkzeug/debug/__init__.py | 25 ++++++++++++++++++++-----
13 src/werkzeug/sansio/utils.py | 2 +-
14 2 files changed, 21 insertions(+), 6 deletions(-)
15
16diff --git a/src/werkzeug/debug/__init__.py b/src/werkzeug/debug/__init__.py
17index 87e68c4..0302b24 100644
18--- a/src/werkzeug/debug/__init__.py
19+++ b/src/werkzeug/debug/__init__.py
20@@ -16,7 +16,9 @@ from zlib import adler32
21
22 from .._internal import _log
23 from ..exceptions import NotFound
24+from ..exceptions import SecurityError
25 from ..http import parse_cookie
26+from ..sansio.utils import host_is_trusted
27 from ..security import gen_salt
28 from ..utils import send_file
29 from ..wrappers.request import Request
30@@ -331,7 +333,7 @@ class DebuggedApplication:
31
32 is_trusted = bool(self.check_pin_trust(environ))
33 html = tb.render_debugger_html(
34- evalex=self.evalex,
35+ evalex=self.evalex and self.check_host_trust(environ),
36 secret=self.secret,
37 evalex_trusted=is_trusted,
38 )
39@@ -359,10 +361,16 @@ class DebuggedApplication:
40 frame: t.Union[DebugFrameSummary, _ConsoleFrame],
41 ) -> Response:
42 """Execute a command in a console."""
43+ if not self.check_host_trust(request.environ):
44+ return SecurityError() # type: ignore[return-value]
45+
46 return Response(frame.eval(command), mimetype="text/html")
47
48 def display_console(self, request: Request) -> Response:
49 """Display a standalone shell."""
50+ if not self.check_host_trust(request.environ):
51+ return SecurityError() # type: ignore[return-value]
52+
53 if 0 not in self.frames:
54 if self.console_init_func is None:
55 ns = {}
56@@ -411,12 +419,18 @@ class DebuggedApplication:
57 return None
58 return (time.time() - PIN_TIME) < int(ts)
59
60+ def check_host_trust(self, environ: WSGIEnvironment) -> bool:
61+ return host_is_trusted(environ.get("HTTP_HOST"), self.trusted_hosts)
62+
63 def _fail_pin_auth(self) -> None:
64 time.sleep(5.0 if self._failed_pin_auth > 5 else 0.5)
65 self._failed_pin_auth += 1
66
67 def pin_auth(self, request: Request) -> Response:
68 """Authenticates with the pin."""
69+ if not self.check_host_trust(request.environ):
70+ return SecurityError() # type: ignore[return-value]
71+
72 exhausted = False
73 auth = False
74 trust = self.check_pin_trust(request.environ)
75@@ -466,8 +480,11 @@ class DebuggedApplication:
76 rv.delete_cookie(self.pin_cookie_name)
77 return rv
78
79- def log_pin_request(self) -> Response:
80+ def log_pin_request(self, request: Request) -> Response:
81 """Log the pin if needed."""
82+ if not self.check_host_trust(request.environ):
83+ return SecurityError() # type: ignore[return-value]
84+
85 if self.pin_logging and self.pin is not None:
86 _log(
87 "info", " * To enable the debugger you need to enter the security pin:"
88@@ -483,8 +500,6 @@ class DebuggedApplication:
89 # form data! Otherwise the application won't have access to that data
90 # any more!
91 request = Request(environ)
92- request.trusted_hosts = self.trusted_hosts
93- assert request.host # will raise 400 error if not trusted
94 response = self.debug_application
95 if request.args.get("__debugger__") == "yes":
96 cmd = request.args.get("cmd")
97@@ -496,7 +511,7 @@ class DebuggedApplication:
98 elif cmd == "pinauth" and secret == self.secret:
99 response = self.pin_auth(request) # type: ignore
100 elif cmd == "printpin" and secret == self.secret:
101- response = self.log_pin_request() # type: ignore
102+ response = self.log_pin_request(request) # type: ignore
103 elif (
104 self.evalex
105 and cmd is not None
106diff --git a/src/werkzeug/sansio/utils.py b/src/werkzeug/sansio/utils.py
107index 1b4d892..7e7b4d2 100644
108--- a/src/werkzeug/sansio/utils.py
109+++ b/src/werkzeug/sansio/utils.py
110@@ -6,7 +6,7 @@ from ..urls import uri_to_iri
111 from ..urls import url_quote
112
113
114-def host_is_trusted(hostname: str, trusted_list: t.Iterable[str]) -> bool:
115+def host_is_trusted(hostname: str | None, trusted_list: t.Iterable[str]) -> bool:
116 """Check if a host matches a list of trusted names.
117
118 :param hostname: The name to check.
119--
1202.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 fc0789a73e..12f6dff17d 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
@@ -13,7 +13,9 @@ LIC_FILES_CHKSUM = "file://LICENSE.rst;md5=5dc88300786f1c214c1e9827a5229462"
13PYPI_PACKAGE = "Werkzeug" 13PYPI_PACKAGE = "Werkzeug"
14 14
15SRC_URI += "file://CVE-2023-25577.patch \ 15SRC_URI += "file://CVE-2023-25577.patch \
16 file://CVE-2023-23934.patch" 16 file://CVE-2023-23934.patch \
17 file://CVE-2024-34069-0001.patch \
18 file://CVE-2024-34069-0002.patch"
17 19
18SRC_URI[sha256sum] = "f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74" 20SRC_URI[sha256sum] = "f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74"
19 21