Jun 15, 2026·9 min read·3 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-48748 is a denial-of-service vulnerability in Netty's HTTP/3 codec (netty-codec-http3) occurring when QPACK dynamic tables are enabled but the blocked streams limit is not explicitly configured. A bug in limit checking and a memory leak in stream tracking allow unauthenticated remote attackers to exhaust the JVM heap memory and crash the server.
CVE-2026-50009 is a cryptographic design vulnerability in the Netty network application framework. Prior to version 4.2.15.Final, the framework's QUIC protocol implementation fails to cryptographically segregate the generated Connection IDs and the associated Stateless Reset Tokens. An on-path network attacker who sniffs traffic during a Connection ID rotation can extract secret token material from cleartext headers, enabling them to inject spoofed reset packets and terminate active connections.
A critical hostname verification bypass vulnerability exists in the Netty network application framework when configured as a TLS client. When a developer registers a custom plain X509TrustManager, Netty wraps it inside an X509TrustManagerWrapper to adapt it to the X509ExtendedTrustManager API. However, this wrapper discards the SSLEngine context, bypassing critical hostname checks. Because the wrapper is identified as an X509ExtendedTrustManager, standard cryptographic engines and Netty's OpenSSL wrappers do not re-wrap it, failing to execute any hostname validation. Consequently, clients silently accept certificates for any host, enabling unauthenticated Man-in-the-Middle (MitM) attacks.
An uncontrolled resource pre-allocation flaw in the Netty Redis codec module allows remote unauthenticated attackers to cause a denial of service (OutOfMemoryError) by sending a crafted Redis Serialization Protocol (RESP) array header.
CVE-2026-50020 is a medium-severity HTTP Request Smuggling/Response Smuggling vulnerability (CWE-444) within the Netty asynchronous network application framework. The flaw resides in Netty's HTTP codec implementation, specifically the HttpObjectDecoder class, which silently consumes arbitrary ISO control bytes preceding the first request line.
CVE-2026-50560 describes a vulnerability in Netty's HTTP/2 codec implementation. When acting as an intermediary (such as a reverse proxy, API gateway, or edge server), Netty can be forced into an application-level Denial-of-Service condition. The attack is triggered by negotiating a restrictive SETTINGS_MAX_HEADER_LIST_SIZE from the client, causing Netty to process incoming requests fully, but subsequently crash or abort during outbound response serialization. This results in an asymmetrical consumption of resources on backend systems and thread starvation within the Netty event loop.