Share
## https://sploitus.com/exploit?id=PACKETSTORM:220639
# CVE-2026-38360: Path Traversal in dash-uploader
    
    [![CVE](https://img.shields.io/badge/CVE-2026--38360-red?style=for-the-badge)](https://www.cve.org/CVERecord?id=CVE-2026-38360)
    [![CWE](https://img.shields.io/badge/CWE-22-orange?style=for-the-badge)](https://cwe.mitre.org/data/definitions/22.html)
    [![Severity](https://img.shields.io/badge/Severity-Critical-red?style=for-the-badge)](#)
    [![Patch](https://img.shields.io/badge/Patch-None-black?style=for-the-badge)](#mitigation)
    [![Auth](https://img.shields.io/badge/Auth-None_required-red?style=for-the-badge)](#attack-vectors)
    [![Version](https://img.shields.io/badge/dash--uploader-0.6.1-blue?style=for-the-badge)](https://pypi.org/project/dash-uploader/)
    [![PyPI Downloads](https://img.shields.io/badge/PyPI%20downloads-28K%2Fmonth-blue?style=for-the-badge)](https://pepy.tech/project/dash-uploader)
    [![Total Downloads](https://img.shields.io/badge/Total%20downloads-733.08K-blue?style=for-the-badge)](https://pepy.tech/project/dash-uploader)
    [![License](https://img.shields.io/pypi/l/dash-uploader?style=for-the-badge)](https://pypi.org/project/dash-uploader/)
    
    Unauthenticated path traversal in [`fohrloop/dash-uploader`](https://github.com/fohrloop/dash-uploader) (Python, PyPI) allowing arbitrary file write, leading to (but not limited to) **Remote Code Execution (RCE)**, application source code overwrite, stored XSS, and persistent backdoor installation.
    
    ### โš ๏ธ No patch is available, and none will ever be released
    
    The repository was [archived on 2025-07-19](https://github.com/fohrloop/dash-uploader/issues/153) with no active maintainer. Every published version (`0.1.0` through `0.7.0a2`) is affected and will remain so. The package still pulls roughly 28,000 monthly downloads.
    
    Anyone running `dash-uploader` in production must apply a mitigation themselves. The recommended fix is to migrate to Plotly Dash's built-in `dcc.Upload` component. See [Mitigation](#mitigation) for full options.
    
    | | |
    |---|---|
    | **CVE ID** | CVE-2026-38360 |
    | **Vulnerability** | Path Traversal (CWE-22) |
    | **CVSS 3.1** | 9.8 / Critical (`AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`) |
    | **Product** | dash-uploader |
    | **Affected versions** | `0.1.0` through `0.7.0a2` (all 18 releases) |
    | **Fixed version** | none (project archived 2025-07-19) |
    | **Attack vector** | Remote, unauthenticated |
    | **Discoverer** | Muhammad Fitri Bin Mohd Sultan |
    | **Assigned by** | MITRE, 2026-05-07 |
    | **Related** | [CVE-2026-38361](https://github.com/a1ohadance/CVE-2026-38361) (DoS in same library) |
    
    ## Description
    
    Three user-controlled parameters from `request.form.get()` in `dash_uploader/httprequesthandler.py` are passed directly to `os.path.join()` and `os.makedirs()` without any sanitization or validation:
    
    1. **`upload_id`** (line 57 โ†’ line 161, `BaseHttpRequestHandler.get_temp_root`): controls the destination directory. An attacker can send `upload_id=../../../../usr/local/lib/python3.10/site-packages` and files are written to Python's package directory.
    
    2. **`resumableFilename`** (line 51 โ†’ line 108, `BaseHttpRequestHandler._post`): controls the final filename. An attacker can traverse out of the upload directory via the filename even with a legitimate `upload_id`.
    
    3. **`resumableIdentifier`** (line 54 โ†’ line 64, `BaseHttpRequestHandler._post`): used with `os.makedirs()` to create the temp directory. An attacker can create arbitrary directories anywhere on the filesystem.
    
    The upload endpoint (`/API/dash-uploader` by default) requires no authentication. The `http_request_handler` hook added in `v0.5.0` allows pre-request checks via `post_before()`, but the vulnerable `_post()` method reads all parameters directly from `request.form` after the hook returns. The hook cannot sanitize parameters before the library processes them. A developer who adds authentication via the hook is still vulnerable to path traversal from an authenticated user.
    
    ## Vulnerable code
    
    ```python
    # dash_uploader/httprequesthandler.py
    def _post(self):
        resumableFilename = request.form.get("resumableFilename", default="error", type=str)
        resumableIdentifier = request.form.get("resumableIdentifier", default="error", type=str)
        upload_id = request.form.get("upload_id", default="", type=str)
        ...
        temp_root = self.get_temp_root(upload_id)                          # upload_id flows in here
        temp_dir = os.path.join(temp_root, resumableIdentifier)            # raw user input -> os.path.join
        if not os.path.isdir(temp_dir):
            os.makedirs(temp_dir)                                          # raw user input -> os.makedirs
    
    def get_temp_root(self, upload_id):
        return os.path.join(self.upload_folder, upload_id)                 # no sanitization: ../../ escapes upload_folder
    ```
    
    Three independent traversal sinks share the same root cause: form values reach `os.path.join` and `os.makedirs` with no validation.
    
    ## Attack vectors
    
    An unauthenticated remote attacker sends an HTTP POST multipart request to the upload endpoint. By injecting path traversal sequences (`../`) into the `upload_id` form parameter, the attacker controls the destination directory for the uploaded file. For example, `upload_id=../../../../usr/local/lib/python3.10/site-packages` writes files to Python's package directory, enabling RCE on the next interpreter startup via `.pth` auto-execution.
    
    No authentication, session token, or CSRF token is required. Two additional parameters (`resumableFilename` and `resumableIdentifier`) provide independent traversal vectors through the same endpoint. The default library configuration as shown in the official quickstart documentation is exploitable with a single `curl` command.
    
    ## Impact
    
    Arbitrary file write to any directory writable by the server process. This translates to **Remote Code Execution (RCE)** through several well-known primitives:
    
    **RCE primitives**
    
    - Python `.pth` file dropped into `site-packages`. Executes attacker-supplied code on the next interpreter startup.
    - `sitecustomize.py` or `usercustomize.py` injection. Executes on every Python startup.
    - Overwriting an importable Python module in the application's package directory. Executes on next import or worker recycle.
    - Overwriting the WSGI/ASGI entry point (e.g. `app.wsgi`, `wsgi.py`). Executes on next worker reload.
    - Cron drop-in (`/etc/cron.d/`, `/etc/cron.hourly/`, user crontab spool) when the process has the necessary privileges. Scheduled execution.
    - Systemd unit or user-unit drop-in (`/etc/systemd/system/`, `~/.config/systemd/user/`). Executes on next service start or reboot.
    - `/etc/ld.so.preload` injection when the process runs as root. Preloads attacker code into every subsequent binary execution.
    - Shell startup file overwrite (`~/.bashrc`, `~/.profile`, `~/.bash_profile`). Executes on next interactive login of the app user.
    - `~/.ssh/authorized_keys` append. Grants persistent SSH access to the host as the app user.
    
    **Web-tier impact**
    
    - Stored cross-site scripting on the host domain by overwriting served JavaScript (for example, Dash framework JS in `site-packages`), affecting every user on every page load until the application is restarted
    - Application source code overwrite (a silent, persistent backdoor that survives normal deploys when the deploy mechanism does not fully overwrite the affected paths)
    
    **Filesystem impact**
    
    - Arbitrary directory creation anywhere the process can reach, via `os.makedirs()` with the unsanitized `resumableIdentifier` parameter (usable for inode exhaustion or for staging writes into nonexistent directory trees)
    - Cross-user file replacement in shared upload directories, leading to data poisoning between tenants of the same application
    
    ## Affected component
    
    - `dash_uploader/httprequesthandler.py`
    - `BaseHttpRequestHandler.get_temp_root()`
    - `BaseHttpRequestHandler._post()`
    
    ## Mitigation
    
    ### โš ๏ธ No patch is available, and the project is archived
    
    Options for currently-deployed users, in order of preference:
    
    1. **Migrate to `dcc.Upload`**, the official upload component shipped with Plotly Dash. Files arrive at the callback as a base64 string; no filesystem-writing handler is exposed and no client-controlled destination path exists, so the bug class here does not apply. Best suited to small and medium files. For very large uploads, see item 2.
    2. **Roll a small Flask upload handler** using `werkzeug.utils.secure_filename()` and a hardcoded server-side destination directory. Never accept client-supplied `upload_id`, filename, or identifier values as path components.
    3. **If continuing to use dash-uploader**, place the upload endpoint behind authentication AND validate `upload_id`, `resumableFilename`, and `resumableIdentifier` against a strict allowlist (e.g., UUIDs only) at a layer that rewrites or rejects the request before the library handler sees it. The library's `http_request_handler` hook does NOT prevent the traversal because parameters are read from `request.form` after the hook returns; sanitization must happen above the library.
    4. **At the reverse-proxy or WAF layer**, reject any request to the upload endpoint where any form field contains `..`, encoded variants (`%2e%2e`, `..%2f`, `%2e%2e%2f`), or absolute paths.
    
    ## Disclosure timeline
    
    | Date | Event |
    |---|---|
    | 2026-03-17 | Vulnerability discovered during security research on a production deployment. |
    | 2026-03-19 | CVE request submitted to MITRE. |
    | 2026-05-07 | CVE-2026-38360 assigned by MITRE. |
    | 2026-05-07 | Public advisory published. |
    
    ## Package context
    
    - Approximately 28,000 monthly downloads on PyPI (27,756 in the 30 days preceding 2026-05-07, with sustained daily volume despite repository archival). Source: [pypistats.org](https://pypistats.org/packages/dash-uploader).
    - Latest published version: `0.6.1` (stable line). Pre-releases extend to `0.7.0a2`.
    - Required dependency: `dash`. Optional dependency: `pyyaml`. License: MIT.
    - 11 dependent packages, 6 dependent repositories.
    - 153 GitHub stars.
    - Repository archived 2025-07-19 ([Issue #153](https://github.com/fohrloop/dash-uploader/issues/153)).
    - No prior CVEs (verified against NVD, GitHub Advisory Database, Snyk, OSV on 2026-03-19).
    
    ## References
    
    - https://github.com/fohrloop/dash-uploader
    - https://pypi.org/project/dash-uploader/
    - https://pypistats.org/packages/dash-uploader
    - https://github.com/fohrloop/dash-uploader/blob/stable/dash_uploader/httprequesthandler.py
    - https://github.com/fohrloop/dash-uploader/blob/dev/dash_uploader/httprequesthandler.py
    - https://github.com/fohrloop/dash-uploader/issues/153
    - https://cwe.mitre.org/data/definitions/22.html
    
    ## Discoverer
    
    Muhammad Fitri Bin Mohd Sultan