FlawOpen

flawopen.com/Teardowns/cve-2026-97662-aws-security-agent-mcp-git-argument-injection

● CVE-2026-97662 · CVSS v4 6.9 · 中危
安全研究 · FlawOpen

CVE-2026-97662:AWS 安全代理 MCP 的 Git 参数注入

CVE-2026-97662 使 AWS Security Agent MCP 差异扫描中的调用方可控 Git 引用被解析为命令选项,而非修订版本,从而绕过工作区限制并在主机上写入文件。请升级到 0.2.0 或更高版本。[1], [2]

CVSS 向量CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N
💡 通俗易懂的原理解析 (ELI5)

有人让办事员按标签找文件夹,但办事员接受了以控制指令开头的标签。档案系统执行指令,而不是查找文件夹。本漏洞中,以短横线开头的 Git 引用可能被当作选项;在启动 Git 前拒绝空引用和类似选项的引用,就能封住这个输入边界。 这是 Git 对选项的解析,不是任意执行 shell 命令。[2]

核心概念与专有名词

MCP 服务器
通过 Model Context Protocol 向 AI 应用提供工具的本地进程。工具参数进入服务器时应视为不可信数据。
参数注入(CWE-88)
命令行程序把不可信输入解释为选项或分隔符;即使调用方传递参数数组而非 shell 字符串,这种情况仍可能发生。[2], [4]
Git 引用
标识提交或树的名称或表达式,例如 main 或 HEAD~1。以 - 开头的值可能被解析为选项。[2], [3]
工作区限制
旨在将工具的文件操作限制在所选项目目录内的边界。如果输入能改变子进程命令的含义,这个边界就不完整。[1], [3]

框架映射

根本原因剖析 (Root Cause)

AWS 描述了差异扫描操作中的上下文相关参数注入:扫描引用可能被解释为命令行选项,而非 Git 修订版本。上游 PR 确认 start_diff_scan 将 base_ref 传入 git diff <base_ref> -- 和 git archive;空值或以短横线开头的值可能因此被误解。这不是 shell 元字符注入:参数数组能避免 shell 解析,但被调用程序仍会解析自身的选项。AWS 表示,该问题可能在预期工作区之外创建、覆盖或截断文件,具体受服务器进程的操作系统权限限制。合并的修复会在调用 Git 前验证 base_ref,拒绝空白值以及去除空格后以 - 开头的值;测试确认被拒绝的输入不会启动子进程或上传。受影响版本为 >=0.1.1 且 <0.2.0;0.2.0 已修复。CVE 记录给出的 CVSS 4.0 为 6.9(中危);GitHub Advisory 另列 CVSS 3.1 8.2(高危)。本文不复现攻击载荷。[1], [2], [3], [4]

攻击执行流程分解

步骤 1

Agent 调用差异扫描工具

本地 MCP 服务器收到可能受调用方影响的 base_ref。风险取决于工作流是否允许不可信仓库、内容或用户影响该值;提示注入本身不会自动进入此代码路径。[2]

步骤 2

该值作为位置参数传给 Git

受影响代码将引用传给 Git diff 和 archive 命令。即使 Python 未使用 shell,以短横线开头的值仍可能被 Git 视为选项。[2], [3]

步骤 3

Git 选项解析突破预期边界

针对该上下文的选项可能把写入重定向到工作区之外。公告记录的影响是创建、覆盖或截断进程权限范围内的文件;实际范围取决于权限和调用流程。[1], [3]

步骤 4

在产生副作用前拒绝不安全引用

0.2.0 会在 Git、S3 上传或 Agent 空间检查前验证 base_ref。请升级,并以最小权限和隔离环境运行 MCP 进程。[1], [3], [6]

边界追踪:修订标识与 Git 选项

这是概念性数据流说明,不是命令序列或漏洞利用复现。

两个解析器,两道独立边界

Shell 解析
参数数组可避免 shell 将该值解释为 shell 语法,从而封闭 shell 解析边界。[3]
Git 选项解析
Git 仍会解析自己的命令行语法。参数数组无法阻止调用方可控的修订标识被解释为选项;应在调用 Git 前校验它。[2], [3]
1

调用方可以影响修订标识

diff scan 工具从工作流接收 base_ref。只有不可信来源能够影响该值时,风险才会出现。[2], [3]

2

参数数组仍会进入 Git 的解析器

Python 可以逐项传参以避免 shell 解析,但 Git 仍会解析自己的命令行选项。[3], [2]

3

工作区隔离假设可能失效

如果 Git 将该值识别为选项,文件输出就可能越出预期工作区;实际范围受 MCP 进程的操作系统权限限制。[1], [2]

4

输入校验封闭了边界

0.2.0 版本会在调用 Git 或产生其他副作用前拒绝空值和类似选项的修订标识。调用方输入不再能选择 Git 的选项行为;有效修订标识仍用于选择所请求的版本。[3], [6]

源代码对比:漏洞与安全实现

这是对上游调用边界的聚焦示例,不是扫描器完整代码。合并的改动会在 Git 调用前验证引用;测试覆盖空值和短横线前缀,并确认不会启动子进程或上传。参数数组可避免 shell 解析,但不能阻止 Git 解析选项。 此代码片段仅展示 git diff 调用;每次使用 base_ref 都必须经过同一验证,包括 git archive。[3]

存在漏洞的实现
def start_diff_scan(path: str, base_ref: str):
    # 类似选项的 ref 值在验证前就会传给 Git。
    return subprocess.run(
        ["git", "diff", base_ref, "--"],
        cwd=path,
        check=False,
    )
加固后的安全修复
def _validate_git_ref(base_ref: str) -> None:
    if not base_ref or not base_ref.strip():
        raise ValueError("base_ref must not be empty.")
    if base_ref.strip().startswith("-"):
        raise ValueError("base_ref must not start with '-'.")

def start_diff_scan(path: str, base_ref: str):
    _validate_git_ref(base_ref)  # 在启动子进程或上传文件之前拒绝。
    return subprocess.run(
        ["git", "diff", base_ref, "--"],
        cwd=path,
        check=False,
    )

工程与系统安全加固清单

参考来源