当攻击者开始使用 AI Agent:企业防线要从“拦截提示词”升级到控制执行链

2026-09-08 36 预计阅读时间: 1 分钟
来源: cloud.google.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:13 分钟

生成式 AI 对攻击者的价值正在发生变化:它不再只是编写邮件、翻译诱饵或生成脚本的聊天工具,而开始成为能够编排扫描器、处理错误、轮换基础设施并整理凭据的自动化控制层。

GTIG 对 2026 年第二季度活动的观察显示,一次云资源失陷后,攻击者在不到六小时内完成了代理化凭据收集活动的规划、构建与执行。真正值得防守方警惕的不是“AI 会不会生成恶意代码”,而是人类介入延迟显著下降后,传统响应窗口被压缩了。

风险已经从单个 Prompt 扩展到整条执行链

传统自动化依赖预先写好的分支:命令失败后,脚本通常退出,等待操作者修复。Agent 工作流则可以读取执行结果、调整参数、切换工具并继续推进任务。报告中的案例已经涉及自动管理漏洞扫描、实时排错、IP 轮换以及大规模凭据整理。

这带来了三个防守变化:

  1. 攻击速度由编排能力决定。 单个模型是否具备突破性能力并非唯一指标。只要模型能稳定调用现有扫描器、云 API 和脚本,多个普通能力也能组合成高速攻击流水线。
  2. 检测对象不再只是恶意载荷。 AGENTS.md、隐藏的 IDE 配置、MCP 服务、工作区启动钩子和 Agent 记忆文件,都可能影响工具下一步执行什么。
  3. 告警必须覆盖机器速度。 如果云审计日志要经过数小时批处理,再由人工排查,攻击者可能已经创建服务账号、导出密钥、扩大配额并部署公开服务。

因此,企业不能只在模型入口部署提示词过滤。模型调用、工具授权、运行时隔离、云控制面变更和数据外传需要形成一条可关联的审计链。

AI 编程供应链出现了新的信任缺口

报告描述的 UNC6780 活动覆盖 PyPI、npm、Docker Hub、GitHub Actions、MCP 组件以及 AI 编程助手。其关键手法不是单纯发布一个名称相似的恶意包,而是利用开发者和 AI 工具共同依赖的“自动信任”:

  • 将文件放入 .claude/.cursor/.vscode/ 等工作区目录;
  • 通过启动命令、构建任务或工作区钩子触发执行;
  • 把恶意 CI 任务伪装成正常的 AI 工具初始化步骤;
  • 窃取 CI/CD 环境中的 OIDC 令牌,以受信发布者身份发布带有效证明的包;
  • 在源代码注释中植入对抗性文本,诱导 LLM 安全扫描器拒绝或跳过后续代码分析。

这里有一个容易忽略的结论:签名和构建证明可以证明“谁通过哪条流水线发布了它”,却不能单独证明发布内容是善意的。 如果发布账号、Runner 或 OIDC 令牌已经失陷,形式上有效的证明仍可能伴随恶意版本。

更稳妥的流水线应同时执行确定性规则、传统静态分析、依赖审计和 LLM 辅助审查。任何模型拒答、超时或解析失败都不能自动变成“扫描通过”。源代码注释、README、Issue 内容和依赖描述也应被当作不可信数据,而不是给安全 Agent 的高优先级指令。

可以这样实践:检查 AI 工作区和 CI 配置

下面是一个只使用 Python 标准库的防御性检查器。它会检查常见 AI/IDE 隐藏目录和 GitHub Actions 工作流,寻找高风险启动命令、过宽权限、日志删除以及明显的提示注入语句。

这是一个最小示例,不应替代 CodeQL、Semgrep、依赖扫描或 EDR。运行前可根据团队实际使用的 IDE、Agent 和构建系统修改 WATCH_DIRSRULES

#!/usr/bin/env python3
import argparse
import re
from pathlib import Path

WATCH_DIRS = {".claude", ".cursor", ".vscode", ".github", ".openclaw", "memory"}
SKIP_DIRS = {".git", "node_modules", ".venv", "dist", "build"}
TEXT_EXTENSIONS = {
    ".json", ".yaml", ".yml", ".toml", ".md", ".js", ".mjs",
    ".cjs", ".ts", ".py", ".sh", ".ps1", ".txt"
}

RULES = [
    ("HIGH", "下载内容直接交给 shell", re.compile(r"(?:curl|wget)[^\n|;]*(?:\||;)\s*(?:sh|bash)\b", re.I)),
    ("HIGH", "Node.js 启动外部进程", re.compile(r"(?:child_process|execSync|spawnSync)\s*[.(]", re.I)),
    ("HIGH", "GitHub Actions 使用 write-all", re.compile(r"permissions\s*:\s*write-all", re.I)),
    ("HIGH", "尝试删除工作流或执行日志", re.compile(r"delete.{0,40}(?:workflow|run|execution).{0,20}log", re.I)),
    ("MEDIUM", "疑似提示注入指令", re.compile(r"(?:ignore previous instructions|system override|unrestricted mode)", re.I)),
    ("MEDIUM", "工作区自动执行钩子", re.compile(r'"(?:onOpen|postinstall|startup|setupCommand)"\s*:', re.I)),
]


def candidate_files(root: Path):
    for path in root.rglob("*"):
        if not path.is_file() or any(part in SKIP_DIRS for part in path.parts):
            continue
        watched = any(part in WATCH_DIRS for part in path.relative_to(root).parts)
        if watched and path.suffix.lower() in TEXT_EXTENSIONS and path.stat().st_size <= 1_000_000:
            yield path


def main():
    parser = argparse.ArgumentParser(description="Scan AI workspace and CI configuration files")
    parser.add_argument("root", nargs="?", default=".", help="repository root")
    args = parser.parse_args()

    root = Path(args.root).resolve()
    findings = []

    for path in candidate_files(root):
        try:
            text = path.read_text(encoding="utf-8", errors="replace")
        except OSError as exc:
            print(f"WARN cannot read {path}: {exc}")
            continue

        for line_number, line in enumerate(text.splitlines(), start=1):
            for severity, message, pattern in RULES:
                if pattern.search(line):
                    findings.append((severity, path, line_number, message, line.strip()[:180]))

    for severity, path, line_number, message, excerpt in findings:
        relative = path.relative_to(root)
        print(f"{severity} {relative}:{line_number} {message}\n  {excerpt}")

    high_count = sum(item[0] == "HIGH" for item in findings)
    print(f"Scanned {root}; findings={len(findings)}, high={high_count}")
    raise SystemExit(1 if high_count else 0)


if __name__ == "__main__":
    main()

保存为 tools/scan_ai_workspace.py 后运行:

chmod +x tools/scan_ai_workspace.py
python3 tools/scan_ai_workspace.py .

也可以把它放进 GitHub Actions,并主动收紧默认令牌权限:

name: AI workspace security check

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false
      - name: Scan AI and IDE workspace configuration
        run: python3 tools/scan_ai_workspace.py .

生产环境还应增加三项控制:对这些隐藏目录启用强制代码所有者审批;阻止来自 Fork 的工作流获取发布权限;把包发布拆分到隔离 Runner,并为 OIDC 受众、仓库、分支和环境设置严格条件。

模型、Prompt、密钥和算力都要进入资产清单

报告中的数据盗窃与勒索活动已开始关注模型、技能定义、Prompt、源代码、训练研究和相关秘密。攻击者还会窃取 AI 编程工具的配置文件,因为其中可能包含明文 API 密钥、模型路由地址和付费额度。

企业至少应把以下对象纳入数据分类和 DLP 策略:

  • 模型权重、微调适配器和评估数据;
  • 系统 Prompt、Agent 指令、技能文件与 RAG 数据源;
  • 模型服务源代码、推理脚本和部署清单;
  • AI 平台 API 密钥、开发者登录态与云服务账号;
  • GPU 配额、AI Workbench、公开推理端点和模型代理服务。

所谓 LLMJacking 也不只是“多出一张云账单”。报告中的案例涉及利用暴露的 GitHub PAT 进入云环境,随后创建服务账号、开放公共服务、查询敏感数据并申请更多高性能计算配额。对应的监控应关注异常启用生成式 AI API、GPU 配额上调、公开访问绑定、外部项目所有者、服务账号密钥导出以及陌生区域中的高算力实例。

不要把 LLM 安全扫描器变成最终裁判

针对扫描器的提示注入说明,安全模型也处于攻击面之内。一个实用的处理原则是:

  • 代码是数据,不是指令。 用明确的数据边界包装待分析内容,不允许仓库中的注释修改系统策略。
  • 拒答不能等于通过。 模型拒绝、输出截断或格式错误应触发人工复核或备用扫描器。
  • 执行与分析分离。 负责阅读代码的模型不应同时拥有直接运行任意命令的长期凭据。
  • 工具权限按任务发放。 Agent 只获得当前仓库、当前环境和短时间内需要的能力。
  • 保留不可篡改日志。 Agent 的提示、工具调用、参数、返回值和身份信息应发送到工作负载之外的日志系统。

这也适用于模型蒸馏防护。报告提到有协调活动通过代理基础设施、欺诈账号和大量凭据发起超大规模查询。自建模型服务至少需要按账号、组织、网络、设备和语义相似度进行联合限流,而不能只限制单个 API Key 的每分钟请求数。

落地时优先缩短防守闭环

AI 并没有让所有攻击都自动变成零日利用。报告明确区分了实际观察与实验性尝试,也没有声称威胁组织已在野外普遍运行完全自主的零日攻击流水线。但代理化编排已经能把公开漏洞、现有工具和被盗云资源组合得更快。

落地时可以按以下顺序推进:

  • 盘点 AI 资产、开发工具配置和可调用的外部工具;
  • 默认禁止 Agent 直接访问生产凭据与长期云密钥;
  • .claude/.cursor/.vscode/、MCP 配置和 CI 工作流执行强制审查;
  • 将模型拒答、工具调用失败和异常重试纳入安全遥测;
  • 为公开服务、IAM 提权、密钥导出和算力扩容建立分钟级响应;
  • 定期演练“开发者账号失陷”和“受信发布流水线失陷”,而不只演练恶意依赖告警。

防守重点应从“阻止一条坏 Prompt”转向“即使 Agent 被诱导,也无法越权、无法静默执行、无法无限扩展”。面对机器速度的攻击链,权限边界和自动化响应比更长的人工审批队列更有效。


相关推荐