CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-24047

Backstage Pass: Breaking Out of the Sandbox with Symlinks

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 22, 2026·7 min read·27 visits

Executive Summary (TL;DR)

CVE-2026-24047 is a path traversal vulnerability in Backstage's `resolveSafeChildPath` function. It arises from improper validation of symbolic links when the target file does not yet exist. Attackers can chain symlinks to escape the intended directory boundary, potentially leading to Remote Code Execution (RCE) by overwriting configuration files or injecting malicious scripts.

A logic flaw in the Backstage framework's path resolution utility allowed attackers to bypass sandbox restrictions using symlinks to non-existent files. By exploiting how the system handled 'phantom' paths, malicious actors could escape the Scaffolder workspace and write files to arbitrary locations on the host filesystem.

The Hook: When Is a Path Not a Path?

Backstage is the darling of Platform Engineering. It's the "portal of portals," designed to unify your infrastructure tooling, services, and documentation into one slick UI. But under the hood, Backstage is doing a lot of heavy lifting—specifically via its Scaffolder. The Scaffolder is essentially a glorified remote file manipulation engine. It clones repos, templating files, moves things around, and pushes code. That sounds a lot like "RCE as a Service" if you aren't careful.

To keep this beast in a cage, Backstage relies on a utility function called resolveSafeChildPath. Its job is simple: ensure that whatever file operations the Scaffolder performs stay strictly within a temporary workspace. It's the bouncer at the club door, checking IDs to make sure no one sneaks into the VIP section (your /etc/shadow file or app-config.yaml).

But here's the thing about filesystems: they aren't just trees; they are graphs. Thanks to symbolic links (symlinks), a path like ./workspace/subdir can instantly teleport you to /root. CVE-2026-24047 is the story of how the bouncer got tricked by a ghost—specifically, how the system failed to validate paths that didn't exist yet.

The Flaw: Trusting the Non-Existent

The vulnerability lives in the gap between intention and reality. When you want to write a file, you usually check if the destination is safe before you write it. Backstage used Node.js's fs.realpathSync to resolve paths to their absolute location, ensuring they started with the safe base directory string.

This works great for files that exist. fs.realpathSync('/safe/base/../../etc/passwd') correctly resolves to /etc/passwd, and the check fails. But what happens if you try to resolve a path for a file you are about to create?

If you ask realpath to resolve /safe/base/symlink_to_root/new_file.txt, and new_file.txt doesn't exist yet, standard implementations often throw an ENOENT error or return the path partially resolved. Backstage's implementation failed to account for dangling symlinks or symlink chains where the final segment is missing.

Because the code couldn't "see" the final file (it wasn't there yet), it essentially shrugged and assumed the path was safe as long as the string manipulation looked okay. This is a classic Time-of-Check to Time-of-Use (TOCTOU) adjacent flaw. The code assumed that if it couldn't prove the path was bad, it must be good. In security, that is a fatal assumption.

The Code: The Smoking Gun

Let's look at the logic. The vulnerable code relied too heavily on the happy path of fs.realpathSync. When an ENOENT (File Not Found) error occurred, the validation logic became permissive or incomplete. It failed to walk up the directory tree to verify the parents.

Here is the corrected logic introduced in the patch (Commit ae4dd5d1). Notice the paranoia level has increased significantly. Instead of giving up when a file is missing, it recursively climbs the directory tree until it finds firm ground.

// The Fix: Recursive Resolution
function resolveRealPath(path: string): string {
  try {
    // 1. Try the standard resolution first
    return realpathSync(path);
  } catch (ex) {
    if (ex.code !== 'ENOENT') {
      throw ex;
    }
  }
 
  // 2. ALERT: The path doesn't exist. 
  // Check if the path ITSELF is a dangling symlink.
  try {
    if (lstatSync(path).isSymbolicLink()) {
      const target = resolvePath(dirname(path), readlinkSync(path));
      // RECURSION: Follow the white rabbit.
      return resolveRealPath(target);
    }
  } catch (ex) { /* ignore */ }
 
  // 3. The file is truly missing. 
  // We must verify the PARENT directory is safe.
  const parent = dirname(path);
  if (parent === path) return path; // Hit root
 
  // RECURSION: Resolve the parent, then append the missing child.
  return resolvePath(resolveRealPath(parent), basename(path));
}

> [!NOTE] > The Fix Strategy: The key change is that ENOENT is no longer an exit strategy. If the leaf node is missing, the code recursively resolves the parent. If parent turns out to be a symlink to /etc, resolveRealPath(parent) will return /etc, and the final check isChildPath('/safe/base', '/etc/new_file') will correctly return false.

The Exploit: Escaping the Matrix

To exploit this, we need to create a situation where we are writing to a file through a symlink, but the file doesn't exist yet so the check passes.

The Attack Scenario

Imagine we have a malicious Scaffolder template. Templates allow us to run shell scripts or file operations. We don't need root; we just need the ability to define a template.

Step 1: The Setup We define a step in our template that creates a symbolic link. This link points to a sensitive directory outside the workspace, like the application's config directory.

# Inside the workspace
ln -s /app/backstage-backend/ malicious_tunnel

Step 2: The Bypass Now, we instruct the Scaffolder to write a file through that tunnel. We ask it to create malicious_tunnel/pwned-config.yaml.

Step 3: The Execution

  1. Backstage calls resolveSafeChildPath('./malicious_tunnel/pwned-config.yaml').
  2. realpath looks for pwned-config.yaml. It's not there.
  3. The vulnerable code catches the ENOENT and fails to fully resolve malicious_tunnel because it was only looking at the full path's existence.
  4. The check passes: "Looks like a valid path inside the workspace!"
  5. The Scaffolder writes the file.
  6. Result: The file is actually written to /app/backstage-backend/pwned-config.yaml.

The Impact: Why Should You Care?

While the CVSS score is a "Medium" 6.3, don't let that fool you. In the right environment, this is a Critical issue. The CVSS metrics often underestimate the impact of file writes in developer tooling.

Configuration Overwrite: The most direct path to total compromise is overwriting app-config.yaml. An attacker could inject a new auth provider (e.g., allowing "guest" access) or change the database connection string to point to a malicious server to steal credentials.

RCE Potential: If the Backstage instance is running scheduled tasks (cron) or using dynamic imports, writing a file to the right place equates to Remote Code Execution. For example, overwriting a script in node_modules or a CI/CD pipeline definition could grant persistent access.

Data Corruption: Even without RCE, an attacker could simply trash the system by overwriting critical system files, causing a Denial of Service. The only thing saving most deployments is that Backstage is typically run as a non-root user (hopefully).

The Fix: Closing the Door

The remediation is straightforward: Update immediately.

Patch Details

  • Component: @backstage/backend-plugin-api
  • Fixed Version: 0.1.17

If you are using @backstage/cli-common directly in your own custom plugins to validate paths, ensure you have pulled the latest version containing commit ae4dd5d.

Defense in Depth

Don't just rely on the patch. This vulnerability highlights why application-level sandboxing is fragile.

  1. Container Hardening: Ensure your Backstage container runs with a Read-Only Root Filesystem, mounting only specific temporary directories as writable.
  2. User Permissions: Restrict who can register new templates in the Scaffolder. If guest users can run arbitrary templates, you are asking for trouble.
  3. Least Privilege: Never run Backstage as root. Ensure the OS user has write access only to the specific directories it needs.

Official Patches

BackstageGitHub Security Advisory

Fix Analysis (1)

Technical Appendix

CVSS Score
6.3/ 10
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Backstage ScaffolderBackstage Backend Plugins using `resolveSafeChildPath`Node.js applications using `@backstage/cli-common` for path validation

Affected Versions Detail

Product
Affected Versions
Fixed Version
@backstage/backend-plugin-api
Backstage
< 0.1.170.1.17
@backstage/cli-common
Backstage
< patched versionCommit ae4dd5d
AttributeDetail
CWE IDCWE-59 (Link Following)
CVSS v3.16.3 (Medium)
Attack VectorNetwork (via Scaffolder Templates)
ImpactArbitrary File Write / Potential RCE
Affected ComponentresolveSafeChildPath
Fix Commitae4dd5d1572a4f639e1a466fd982656b50f8e692

MITRE ATT&CK Mapping

T1566Phishing (via malicious Template)
Initial Access
T1059Command and Scripting Interpreter
Execution
T1202Indirect Command Execution (via Config Overwrite)
Defense Evasion
CWE-59
Link Following

Improper Link Resolution Before File Access ('Link Following')

Known Exploits & Detection

HypotheticalExploitation involves creating a Scaffolder template that symlinks to a sensitive directory and writes a new file through that link.

Vulnerability Timeline

Fix committed to master
2026-01-20
CVE Published / Advisory Released
2026-01-21

References & Sources

  • [1]GHSA Advisory
  • [2]NVD Entry

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•33 minutes ago•CVE-2026-58197
8.8

CVE-2026-58197: Host Escape and Lateral Movement via Insecure Container Network Defaults in ToolHive

A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.

Amit Schendel
Amit Schendel
2 views•8 min read
•about 2 hours ago•CVE-2026-63405
5.9

CVE-2026-63405: Insufficient Verification of Data Authenticity in AnyCable Pusher REST API

AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.

Amit Schendel
Amit Schendel
3 views•5 min read
•about 3 hours ago•CVE-2026-64847
6.8

CVE-2026-64847: Indefinite Denial of Service via Undrained Stderr in AnyIO Process Pool Workers

A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.

Amit Schendel
Amit Schendel
5 views•6 min read
•about 4 hours ago•CVE-2026-63349
7.0

CVE-2026-63349: Privilege Dropping Bypass and Denial of Service in AnyIO Subprocess Module

CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.

Alon Barad
Alon Barad
7 views•5 min read
•about 5 hours ago•CVE-2026-63406
5.9

CVE-2026-63406: Information Disclosure via Insecure Telemetry and Hardcoded Credentials in AnyCable-Go

CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.

Alon Barad
Alon Barad
5 views•5 min read
•about 6 hours ago•CVE-2026-84992
6.1

CVE-2026-84992: Cross-Site Scripting (XSS) via Fenced Code Block Parsing in md-editor-v3

CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.

Amit Schendel
Amit Schendel
8 views•6 min read