Jun 15, 2026·9 min read·42 visits
Unsanitized input interpolation in the AWS CDK NodejsFunction bundling component allows unauthenticated local command execution during infrastructure synthesis (cdk synth).
A critical supply-chain OS command injection vulnerability exists in the NodejsFunction local bundling pipeline within the AWS Cloud Development Kit (CDK) library (aws-cdk-lib) before version 2.245.0 (and before 2.246.0 on Windows systems). The vulnerability allows a threat actor who can control any of several bundling properties (externalModules, define, loader, inject, or esbuildArgs) to execute arbitrary operating system commands on the host machine running the CDK compilation or deployment toolchain (e.g., during cdk synth, cdk deploy, or cdk diff).
The AWS Cloud Development Kit (CDK) is an open-source software development framework to define cloud infrastructure in code and provision it through AWS CloudFormation. Within this framework, the NodejsFunction construct (under the @aws-cdk/aws-lambda-nodejs package or aws-cdk-lib/aws-lambda-nodejs) abstracts the process of compiling, bundling, and packaging JavaScript or TypeScript code for AWS Lambda. Under the hood, this construct leverages esbuild to compile target source entries, bundle associated dependencies, and output an archive optimized for cloud deployment. This pipeline executes automatically during the synthesis phase, which converts high-level programming language code into CloudFormation templates.
A critical supply-chain OS command injection vulnerability (classified as CWE-78) exists in the local bundling implementation of this construct. An attacker who can control specific configuration parameters can inject malicious OS shell metacharacters. Because these parameters are subsequently passed directly to the local shell environment during compilation, the payload executes on the host workstation or automated build runner. This execution operates with the execution privileges of the user initiating the synthesis process, typically a developer or a highly privileged continuous integration (CI) service.
The attack surface is particularly severe because the vulnerability manifests during infrastructure synthesis rather than deployment or runtime. Standard client-side secure-install configurations, such as utilizing the --ignore-scripts flag during package installation, fail to mitigate this threat because the code executes within standard TypeScript code paths rather than lifecycle hooks. Consequently, this vulnerability opens up significant supply-chain pathways. Threat actors can exploit the vulnerability by publishing malicious packages containing custom wrapper constructs, or by submitting pull requests containing modified bundling configurations to open-source repositories.
The vulnerability stems from the direct concatenation of user-supplied bundling parameters into an operating system shell command. When NodejsFunction utilizes local bundling, the AWS CDK CLI runtime attempts to invoke esbuild locally rather than spawning a Docker container. The underlying logic builds a shell command dynamically using an array of arguments representing various compiler flags and values. Unescaped configuration parameters are directly interpolated into this argument array.
Specifically, the vulnerable code in bundling.ts processes configuration fields including externalModules, loader, define, inject, and esbuildArgs. These fields allow developers to customize the behavior of the compilation process by specifying external dependencies, file loaders, preprocessor definitions, and raw arguments. The code constructs an array of strings representing the final command line, joining the array with spaces to form a single execution string. This raw string is then passed to a process execution wrapper that spawns /bin/bash -c on POSIX systems or cmd.exe /c on Windows systems.
Because the runtime performs no validation, escaping, or sanitization on these configuration fields, shell metacharacters such as &, ;, |, or backticks passed within these strings act as command separators. When the shell interprets the compiled execution string, it reads these characters as instructions to split the commands. This results in the sequential or parallel execution of the original compiler call and the injected attacker payload. The command executes under the same environment context and privileges as the CDK CLI process.
To address this vulnerability, the AWS CDK engineering team released a sequence of security updates. The primary fix, implemented in Pull Request #37292, completely refactors how local execution occurs. The vulnerability mitigation centers on migrating from shell-based command string execution to direct process spawning. By passing an array of arguments to Node's child_process.spawnSync with { shell: false }, the operating system loads the executable binary directly without executing a shell interpreter. This ensures that command line arguments are passed as discrete strings to the target program's execution vector rather than parsed by a command shell, preventing command injection.
// Pre-Patch Code (aws-lambda-nodejs/lib/bundling.ts)
// Dynamic assembly of a single CLI command string via interpolation
const esbuildCommand: string[] = [
options.esbuildRunner,
'--bundle', `"${relativeEntryPath}"`,
`--target=${this.props.target ?? toTarget(scope, this.props.runtime)}`,
'--platform=node',
...this.externals.map(external => `--external:${external}`),
...loaders.map(([ext, name]) => `--loader:${ext}=${name}`),
...defines.map(([key, value]) => `--define:${key}=${JSON.stringify(value)}`),
...this.props.inject ? this.props.inject.map(i => `--inject:"${i}"`) : [],
...this.props.esbuildArgs ? [toCliArgs(this.props.esbuildArgs)] : [],
];
const localCommand = esbuildCommand.join(' ');
exec(
osPlatform === 'win32' ? 'cmd' : 'bash',
[osPlatform === 'win32' ? '/c' : '-c', localCommand],
{ env: { ...process.env }, cwd }
);// Patched Code (aws-lambda-nodejs/lib/bundling.ts)
// Transitioned to structured BundlingStep types avoiding direct shell execution
type BundlingStep =
| { type: 'shell'; commands: string[] }
| { type: 'spawn'; command: string[]; cwd?: string }
| { type: 'callback'; operation: () => void };
// Execution of the 'spawn' step bypasses the shell entirely
if (step.type === 'spawn') {
const [command, ...args] = step.command;
const result = spawnSync(command, args, {
cwd: step.cwd ?? cwd,
env: { ...process.env, ...environment },
stdio: ['ignore', 'pipe', 'pipe'],
});
if (result.status !== 0) {
throw new Error(`Command failed with status ${result.status}`);
}
}A secondary patch, Pull Request #37412, was introduced to resolve an operating system regression. On Windows systems running Node.js v22 or higher, executing .cmd or .bat shims directly via spawnSync without shell wrapping triggers an OS-level EINVAL error. To resolve this while preventing command injection, the patch routes execution through powershell.exe but strictly escapes all arguments using a new powershellEscape utility. This ensures arguments are treated strictly as string literals inside the PowerShell environment.
// PowerShell Escaping Utility in PR #37412
function powershellEscape(arg: string): string {
// Escape single quotes inside single-quoted strings by doubling them
return "'" + arg.replace(/'/g, "''") + "'";
}
if (osPlatform === 'win32') {
// Route via powershell and join arguments using the call operator
exec('powershell.exe', [
'-NoProfile',
'-Command',
`& ${step.command.map(powershellEscape).join(' ')}`
], {
cwd: step.cwd ?? cwd,
env: { ...process.env, ...environment }
});
}Exploitation of CVE-2026-11417 requires an attacker to inject a shell metacharacter into one of the configurable properties of the NodejsFunction bundling properties. This control can be achieved via a direct modification in a pull request to a target repository, or by publishing a malicious npm package containing a custom CDK construct wrapper that inherits from NodejsFunction and injects the payload under the hood. The target system must then perform infrastructure synthesis.
To demonstrate this behavior, a simple proof of concept can be established by configuring a stack containing a single NodejsFunction. By manipulating the externalModules parameter within the bundling configuration, an attacker can append a command separator and an arbitrary operating system command. When cdk synth is executed, the injection triggers immediately.
import * as cdk from 'aws-cdk-lib';
import { Stack } from 'aws-cdk-lib';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import * as path from 'path';
export class VulnerableStack extends Stack {
constructor(scope: cdk.App, id: string) {
super(scope, id);
new NodejsFunction(this, 'ExploitTarget', {
entry: path.join(__dirname, 'lambda', 'handler.ts'),
runtime: Runtime.NODEJS_20_X,
bundling: {
// Injection payload uses the '&' shell metacharacter
// This runs the shell command immediately following esbuild invocation
externalModules: ['aws-sdk & echo "EXPLOIT_SUCCESSFUL" > /tmp/pwned.txt'],
},
});
}
}Executing cdk synth on a system running an affected version of aws-cdk-lib results in the execution of the injected command block. The shell separates the executing processes, running the compiler and then executing the second command. This creates the file /tmp/pwned.txt on the host, demonstrating successful local remote code execution.
The impact of this command injection vulnerability is substantial due to its positioning in the software supply chain. While standard runtime vulnerabilities threaten the production environment, a synthesis-time vulnerability targets the development workstation and the deployment pipelines directly. An attacker who successfully exploits this flaw can execute arbitrary commands with the privileges of the active developer or the continuous integration (CI/CD) runner, gaining complete compromise over the underlying operating system environment.
In typical modern cloud deployments, developer machines and CI/CD pipelines hold highly sensitive credentials, such as active AWS session tokens, long-lived AWS IAM access keys, SSH keys, and access tokens for internal code repositories. Gaining execution on these runners allows an attacker to access environment variables, retrieve stored configuration secrets, and exfiltrate credentials to an external server. Because the execution runs within a trusted build phase, the outbound network requests generated by the payload might bypass egress firewall restrictions configured for production environments.
Additionally, access to the synthesis environment allows an attacker to manipulate the generated CloudFormation templates. This permits subtle modifications to infrastructure definitions, such as altering security group configurations to expose database ports, modifying IAM policy definitions to grant excessive permissions, or injecting malicious backdoors into the built Lambda deployment packages. The vulnerability therefore bridges local workstation compromise to complete cloud architecture compromise.
The definitive mitigation for this vulnerability is upgrading the AWS CDK library to a patched version. For applications running on Linux or macOS workstations, upgrade aws-cdk-lib to version 2.245.0 or higher. For applications built on Windows systems, upgrade aws-cdk-lib to version 2.246.0 or higher to resolve compatibility issues with direct process spawning on Node.js v22+ environments.
In environments where upgrading the library is not immediately possible, organizations can enforce defensive controls to mitigate the risk. The vulnerability can be neutralized by forcing the use of Docker-based bundling rather than local bundling. This is achieved by ensuring that Docker is running and available to the CDK CLI, or by modifying the construct configuration to explicitly utilize containerized environments. When bundling runs inside an isolated Docker container, the command injection is constrained to the container workspace, preventing access to the host workstation's file system and environmental secrets.
Furthermore, organizations should restrict the permissions allocated to CI/CD runners during the synthesis phase. The generation of CloudFormation templates via cdk synth is a purely local compilation task that does not require direct access to active AWS credentials or cloud infrastructure endpoints. Separating the synthesis step into an isolated, unprivileged build phase that lacks cloud deployment credentials significantly limits the blast radius of a potential exploitation attempt. Implement static analysis and linting rules to flag or reject pull requests that modify bundling properties inside CDK infrastructure code.
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
aws-cdk-lib AWS | < 2.245.0 | 2.245.0 |
aws-cdk-lib (Windows) AWS | < 2.246.0 | 2.246.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78 |
| Attack Vector | Local |
| CVSS Score | 7.3 (CVSS:3.1) |
| EPSS Score | 0.00657 (Percentile: 46.42%) |
| Impact | Unauthenticated OS Command Execution |
| Exploit Status | Proof of Concept / Public Exploit Code Available |
| CISA KEV Status | Not Listed |
The software constructs an OS command using externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements that can modify the intended OS command when it is sent to a downstream component.
CVE-2026-67427 is a capability bypass vulnerability in the Flyto2 Core workflow execution kernel. Due to a logical inconsistency in how dynamic parameters are resolved, the system evaluates environment variables via template interpolation prior to executing capability filter validation. This permits unprivileged workflow definitions to completely bypass denylist restrictions on the `env.get` module, exfiltrating critical host configurations, API tokens, and credentials via allowed communication channels.
An insecure credential forwarding vulnerability in Flyto2 Core prior to version 2.26.6 allows attackers to exfiltrate operator API keys. This occurs because the system forwards environment-derived API keys to user-controlled custom endpoints, bypassing SSRF guards designed only for private target validation.
A critical remote code execution (RCE) vulnerability exists in AWS Amplify Studio's code-generation library (@aws-amplify/codegen-ui). An authenticated attacker with permissions to create or modify component schemas can inject malicious JavaScript code into those schemas. When the Amplify CLI or the build environment processes these schemas, the unvalidated expressions are executed within the host Node.js environment, leading to full system compromise.
CVE-2026-67426 is a critical vulnerability in Flyto2 Core prior to version 2.26.7. The standalone flyto-verification service binds to all interfaces (0.0.0.0) on port 8344 and exposes an unauthenticated POST /run endpoint. This endpoint accepts an arbitrary client-controlled callback URL and makes an outbound POST request containing the sensitive internal runner secret in the headers. Attackers can exploit this to retrieve the FLYTO_RUNNER_SECRET and perform Server-Side Request Forgery (SSRF) against internal network targets.
CVE-2026-66066 (popularly known as 'KindaRails2Shell') is a critical security vulnerability in the Active Storage component of Ruby on Rails. The vulnerability arises from an insecure default integration with the libvips image processing library via the ruby-vips gem. Under default configurations, Active Storage fails to restrict untrusted format loaders within libvips, allowing remote, unauthenticated attackers to upload malformed files that leverage external dataset features to read local server files. By extracting cryptographic secrets such as SECRET_KEY_BASE from the leaked file contents, attackers can forge signed Marshal serialization payloads to achieve remote code execution.
An SSRF validation bypass exists in dssrf-js (v1.0.3 and prior) due to an improper string normalization sequence inside is_url_safe. Before validating the host using Node's WHATWG parser, the helper strips the '@' symbol. This corrupts the parser's authority resolution, while the application's client requests the original, un-sanitized string containing internal IP targets.