CVE-2024-49766
NoneCVSS
—
EPSS
0.8%
Published
Oct 25, 2024
CISA due date
—
Cloudanix analysis
Some Windows-based Python web applications may let a crafted file request access locations they were not supposed to expose.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Moderate
- Impact
- Data Exposure
- Component
- Library
- Class
- Path Traversal
- Remediation
- Upgrade
- Effort
- Low
- Patch risk
- Low
What it actually does
Werkzeug safe_join() can incorrectly treat Windows UNC-style paths such as //server/share as non-absolute when running on Python versions earlier than 3.11 on Windows. If an application passes attacker-controlled path components to safe_join(), the resulting path may escape the intended base directory and resolve to an unintended UNC location. This can allow unauthorized file access through application file-serving functionality and may also cause the application server to initiate outbound SMB/UNC access if the crafted path references a remote share. Applications running on non-Windows platforms or Python 3.11 and later are not affected by this specific issue.
Why it matters to the business
The main business risk is unintended disclosure of files through affected Python web applications that use Werkzeug path joining for downloads, static file access, uploads, or template/resource lookup. In Windows deployments, a crafted request could cause the application to read from locations outside the intended directory or reach out to attacker-controlled network shares, potentially exposing sensitive data or credentials. The issue is not currently in CISA KEV, has low EPSS, and has no known public exploit maturity in the supplied data, so urgency is highest for internet-facing Windows applications on Python versions before 3.11 that accept user-controlled file paths.
How an attack unfolds
An attacker first identifies a web endpoint that accepts a filename or path and is implemented using Werkzeug safe_join(), commonly a download, static file, attachment, upload retrieval, or resource-serving route. If the application runs on Windows with Python earlier than 3.11 and a vulnerable Werkzeug version, the attacker submits a path beginning with a UNC-style double slash, such as //server/share/file, or an encoded equivalent. Because the absolute-path check does not correctly reject that form, safe_join() may return a path outside the intended safe directory. The application then opens or serves the resolved path. Depending on the application and host permissions, this may return unintended data, trigger access to an internal network share, or cause an outbound SMB connection to an attacker-controlled host.
How to fix it
Upgrade Werkzeug to version 3.0.6 or later, or apply the upstream fix commit 5eaefc3996aa5cc8c5237d8b82f1b89eed6ea624 where consuming directly from source. Where feasible, run affected applications on Python 3.11 or later, which is not affected by the underlying os.path.isabs() UNC-path behavior described here. Prioritize Windows deployments that use Werkzeug safe_join() with user-controlled path input. As defense in depth, reject path inputs that begin with /, \, //, \\, contain encoded slash/backslash sequences, contain drive letters, or normalize outside the expected base directory. Avoid allowing raw user input to select filesystem paths; use opaque object identifiers mapped server-side to known files. Restrict outbound SMB from application servers to prevent UNC path exploitation from reaching attacker-controlled shares.
How to confirm the fix worked
Confirm package inventory shows Werkzeug 3.0.6 or later, or the upstream fixed commit 5eaefc3996aa5cc8c5237d8b82f1b89eed6ea624. Confirm affected services are not running on Windows with Python earlier than 3.11, or that all such services have been patched. Review code for direct or indirect use of werkzeug.utils.safe_join() with request-controlled values. In a non-production test environment, send benign UNC-style inputs such as //nonexistent-test-host/share/file and encoded equivalents to file-serving routes; fixed or safely coded applications should reject the input and should not attempt outbound SMB/DNS resolution. Verify egress controls prevent the web application process from connecting to TCP/445 on unapproved hosts.
Typical attack path
- Find an application endpoint that accepts a user-supplied filename, path, or resource identifier.
- Determine or infer that the application is a Python/Werkzeug application running on Windows with Python earlier than 3.11.
- Submit UNC-style path input such as //host/share/file or URL-encoded variants in the vulnerable parameter or path segment.
- Rely on vulnerable safe_join() behavior to bypass the intended base-directory restriction.
- Cause the application to open, return, or otherwise interact with the unintended UNC path.
- Collect exposed file contents, observe outbound SMB/DNS activity, or iterate over path values to find accessible resources.
How to detect it
Review HTTP access logs, application logs, WAF events, and reverse proxy logs for requests to file-serving or download endpoints containing UNC-style paths or encoded slash/backslash sequences. Focus on parameters or path segments containing leading double slashes, double backslashes, %2f%2f, %5c%5c, or mixed encoded separators. Correlate suspicious requests with application errors mentioning UNC paths, network path failures, or Windows file access errors. On Windows hosts, examine EDR/Sysmon telemetry for python.exe, wpython.exe, flask.exe, gunicorn-equivalent service wrappers, w3wp.exe, or other application worker processes attempting outbound connections to TCP/445 or accessing filenames beginning with \\ or //. Review DNS logs for lookups matching hostnames embedded in suspicious UNC inputs. Check firewall, proxy, and SMB client logs for outbound SMB attempts from web application servers to untrusted internal or external systems. Because no specific public exploit indicators are supplied, detection should be behavior-based and focused on crafted path input plus unexpected UNC/SMB activity.
- //server/share
- \\server\share
- %2f%2f
- %2F%2F
- %5c%5c
- %5C%5C
- ..%2f%2f
- ..%5c%5c
- file=//
- path=//
- filename=//
- download=//
- attachment=//
- resource=//
- file=%2f%2f
- path=%2f%2f
- filename=%2f%2f
- file=%5c%5c
- path=%5c%5c
- filename=%5c%5c
- WinError 53
- The network path was not found
- UNC path
- TargetFilename=\\
- DestinationPort=445
- Image=python.exe
- Image=w3wp.exe
What your security team should do
- Inventory Python web applications using Werkzeug, especially Flask-based services, and identify those running on Windows with Python versions earlier than 3.11.
- Upgrade Werkzeug to 3.0.6 or later, or apply commit 5eaefc3996aa5cc8c5237d8b82f1b89eed6ea624 for source-based deployments.
- Prioritize externally reachable endpoints that serve files, attachments, uploaded content, or static resources using user-controlled path values.
- Add application-layer validation that rejects UNC paths, absolute paths, encoded slashes/backslashes, drive-letter paths, and traversal sequences before path resolution.
- Block outbound SMB from application servers except to explicitly approved file servers.
- Search web, application, DNS, proxy, firewall, EDR, and Windows logs for UNC-style path probes and outbound SMB connections from Python or web worker processes.
- Add regression tests for safe path handling, including //server/share, \\server\share, %2f%2fserver%2fshare, and %5c%5cserver%5cshare inputs.
The record
Werkzeug is a Web Server Gateway Interface web application library. On Python < 3.11 on Windows, os.path.isabs() does not catch UNC paths like //server/share. Werkzeug's safe_join() relies on this check, and so can produce a path that is not safe, potentially allowing unintended access to data. Applications using Python >= 3.11, or not using Windows, are not vulnerable. Werkzeug version 3.0.6 contains a patch.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| PyPI | werkzeug | 0 | 3.0.6 |
| Chainguard | airflow-2 | 0 | 2.11.1-r0 |
| Chainguard | airflow-2-compat | 0 | 2.11.0-r15 |
| Chainguard | airflow-2-iamguarded-compat | 0 | 2.11.0-r15 |
| Chainguard | airflow-3 | 0 | 3.0.1-r1 |
| Chainguard | airflow-3-bitnami-compat | 0 | 3.0.1-r1 |
| Chainguard | airflow-3-compat | 0 | 3.0.1-r1 |
| Chainguard | airflow-3-compat | 0 | 3.1.2-r0 |
| Chainguard | airflow-3-iamguarded-compat | 0 | 3.1.2-r0 |
| Chainguard | airflow-core-2 | 0 | 2.11.1-r0 |
| Chainguard | airflow-core-2-compat | 0 | 2.11.0-r8 |
| Chainguard | airflow-core-2-oci-entrypoint | 0 | 2.11.0-r8 |
| Chainguard | emissary | 0 | 3.9.1-r4 |
| Chainguard | emissary-apiext | 0 | 3.9.1-r4 |
| Chainguard | emissary-oci-entrypoint | 0 | 3.9.1-r4 |
| Chainguard | kubeflow-jupyter-web-app | 0 | 1.9.2-r1 |
| Chainguard | kubeflow-pipelines-visualization-server | 0 | 2.4.0-r0 |
| Chainguard | kubeflow-volumes-web-app | 0 | 1.9.2-r1 |
| Chainguard | mlflow | 0 | 2.21.0-r0 |
| Chainguard | mlflow-bitnami | 0 | 2.21.0-r0 |
| Chainguard | mlflow-iamguarded-compat | 0 | 2.21.0-r0 |
| Chainguard | py3-supported-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | py3-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | py3.10-ambassador | 0 | 3.9.1-r4 |
| Chainguard | py3.10-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | py3.11-ambassador | 0 | 3.9.1-r4 |
| Chainguard | py3.11-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | py3.12-ambassador | 0 | 3.9.1-r4 |
| Chainguard | py3.12-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | py3.13-ambassador | 0 | 3.9.1-r4 |
| Chainguard | py3.13-werkzeug | 0 | 3.0.6-r0 |
| Chainguard | superset | 0 | 4.0.2-r6 |
| Chainguard | superset-ci | 0 | 4.0.2-r6 |
| Chainguard | superset-entrypoint | 0 | 4.0.2-r6 |
| Chainguard | superset-iamguarded-compat | 0 | 4.0.2-r6 |
| Ubuntu:20.04:LTS | python-werkzeug | 0 | No fix |
| Ubuntu:22.04:LTS | python-werkzeug | 0 | No fix |
| Ubuntu:24.04:LTS | python-werkzeug | 0 | No fix |
| Ubuntu:24.10 | python-werkzeug | 0 | No fix |
| Ubuntu:Pro:16.04:LTS | python-werkzeug | 0 | No fix |
| Ubuntu:Pro:18.04:LTS | python-werkzeug | 0 | No fix |
| Wolfi | airflow-3 | 0 | 3.0.1-r1 |
| Wolfi | airflow-3-bitnami-compat | 0 | 3.0.1-r1 |
| Wolfi | airflow-3-compat | 0 | 3.0.1-r1 |
| Wolfi | airflow-3-compat | 0 | 3.1.2-r0 |
| Wolfi | airflow-3-iamguarded-compat | 0 | 3.1.2-r0 |
| Wolfi | emissary | 0 | 3.9.1-r4 |
| Wolfi | emissary-apiext | 0 | 3.9.1-r4 |
| Wolfi | emissary-oci-entrypoint | 0 | 3.9.1-r4 |
| Wolfi | kubeflow-jupyter-web-app | 0 | 1.9.2-r1 |
| Wolfi | kubeflow-pipelines-visualization-server | 0 | 2.4.0-r0 |
| Wolfi | kubeflow-volumes-web-app | 0 | 1.9.2-r1 |
| Wolfi | mlflow | 0 | 2.21.0-r0 |
| Wolfi | mlflow-bitnami | 0 | 2.21.0-r0 |
| Wolfi | mlflow-iamguarded-compat | 0 | 2.21.0-r0 |
| Wolfi | py3-supported-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | py3-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | py3.10-ambassador | 0 | 3.9.1-r4 |
| Wolfi | py3.10-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | py3.11-ambassador | 0 | 3.9.1-r4 |
| Wolfi | py3.11-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | py3.12-ambassador | 0 | 3.9.1-r4 |
| Wolfi | py3.12-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | py3.13-ambassador | 0 | 3.9.1-r4 |
| Wolfi | py3.13-werkzeug | 0 | 3.0.6-r0 |
| Wolfi | superset | 0 | 4.0.2-r6 |
| Wolfi | superset-ci | 0 | 4.0.2-r6 |
| Wolfi | superset-entrypoint | 0 | 4.0.2-r6 |
| Wolfi | superset-iamguarded-compat | 0 | 4.0.2-r6 |
Related vulnerabilities
- CVE-2026-27199 Werkzeug is a comprehensive WSGI web application library. Also affects werkzeug
- CVE-2026-21860 Werkzeug is a comprehensive WSGI web application library. Also affects werkzeug
- CVE-2025-66221 Werkzeug safe_join() allows Windows special device names Also affects werkzeug
- CVE-2024-49767 Werkzeug is a Web Server Gateway Interface web application library. Also affects werkzeug
- CVE-2023-46136 Werkzeug is a comprehensive WSGI web application library. Also affects werkzeug
- CVE-2016-10516 Cross-site scripting (XSS) vulnerability in the render_full function in debug/tbtools.py… Also affects werkzeug
- CVE-2026-4539 A security flaw has been discovered in pygments up to 2.19.2. Also affects airflow-2
- CVE-2026-30922 pyasn1 is a generic ASN.1 library for Python. Also affects airflow-2