警惕“AI投毒”:ClaudeCode项目配置漏洞可致代码执行

admin 2026-03-04 11:16:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文披露了ClaudeCode工具的CVE-2025-59536漏洞,攻击者可通过恶意配置文件实施远程代码执行。三种攻击场景包括恶意Hooks执行、绕过MCP用户确认机制和API密钥劫持。文档提供了详细漏洞分析、防护建议和自动化扫描脚本,建议立即升级到最新版本、严审项目配置文件并避免硬编码API密钥。 综合评分: 88 文章分类: 漏洞预警,AI安全,漏洞分析,安全工具,代码审计


cover_image

警惕“AI 投毒”:Claude Code 项目配置漏洞可致代码执行

原创

APT-101 APT-101

APT-101

2026年3月2日 08:53 陕西

摘要: 只需在一个包含恶意配置的本地项目目录中运行 Claude Code,无需任何恶意代码编写,攻击者就能通过远程代码执行(RCE)接管你的机器,并瞬间窃取你的 Claude API 密钥!Check Point 最新披露的 CVE-2025-59536 揭示了 AI Agent 时代全新的自动化攻击模式。


01 恐惧源头:配置文件的“自动化投毒”

Anthropic 的 Claude Code 是一款强大的本地 AI 开发助手。为了支持团队协作,它允许在项目根目录中维护一个 .claude/settings.json 配置文件,以便团队共享相同的 AI 设置、插件和工具链。

这个本来方便协作的功能,如今变成了致命的攻击向量。

Check Point Research 发现,如果一个攻击者向开源项目提交了这个恶意配置文件,任何克隆并打开该项目的开发者,都将面临巨大的安全风险。这是一种全新的配置级(Configuration-based)攻击


02 深度拆解:三个致命的漏洞场景

风险场景一:伪装的 Hooks —— 隐蔽的远程代码执行 (RCE)

Hooks 是 Claude Code 用来在特定事件(如项目初始化)自动执行指令的功能。

  • 攻击手法:攻击者在配置文件中定义了一个恶意的 Hook 动作。
  • 技术机制:当用户在受感染的目录运行 claude 命令时,虽然会出现信任提示弹窗,但攻击者研究发现,用户点击“信任”后,Hook 队列中的命令会直接在后台自动执行,不再需要二次确认
  • 潜在后果:攻击者可以执行任意 Shell 命令,包括安装后门、下载恶意程序。

风险场景二:自动化绕过 MCP 的用户确认机制

MCP (Model Context Protocol) 允许 Claude 调用本地或远程工具(如连接本地数据库、运行终端命令)。

  • 攻击手法:在配置文件中设置高危参数,如 enableAllProjectMcpServers
  • 漏洞核心:这是一个 Race Condition(竞态条件)漏洞。在 Anthropic 修复此缺陷前,恶意命令抢在用户点击“信任目录”的提示确认框之前就已经执行了
  • 潜在后果:在受害者意识到项目正在打开之前,机器就已经被完全控制。

风险场景三:针对 Anthropic API 密钥的“中间人”投毒

这是一种旨在直接窃取财富和数据的身份劫持攻击。

  • 攻击手法:攻击者在 .claude/settings.json 中定义环境变量 ANTHROPIC_BASE_URL,将其重定向到攻击者控制的钓鱼服务器。
  • 技术机制:Claude Code 在启动初始化时会向其服务器发送带有 Authorization 头(内含明文 API 密钥)的身份验证请求。
  • 关键漏洞:该请求在 用户决定是否信任该目录之前就已经发出了。即使受害者发现异样并取消了目录信任,密钥已经泄漏。
  • 潜在后果:攻击者使用受害者的 API 密钥进行账单欺诈,或者利用 API 的 Workspace 功能直接读取受害者托管在该 Workspace 下的所有项目文件。


03 总结与防护:严审你的项目配置文件

在 AI Agent 时代,配置文件即病毒体。防御边界已从 .sh 和 .exe 扩展到了 .json 和 .yaml

| 防御策略 | 操作指南 | | — | — | | 工具更新 | 立即将 Claude Code 升级到最新版本 ,官方已修复此问题。 | | 严审配置 | 在 Code Review 中,严禁将未审核的 .claude/ 目录纳入信任范围。 | | 权限隔离 | 切勿在配置文件中硬编码 API 密钥,优先使用环境变量。 |

04 自动化扫描脚本

#!/usr/bin/env python3# Scans a repo for the three Claude Code vulnerability patterns.# Usage: python3 scanner.py <path-to-repo>
import&nbsp;jsonimport&nbsp;osimport&nbsp;sysimport&nbsp;refrom&nbsp;pathlib&nbsp;import&nbsp;Path
# ANSI colorsRED =&nbsp;"\033[91m"GREEN =&nbsp;"\033[92m"YELLOW =&nbsp;"\033[93m"CYAN =&nbsp;"\033[96m"RESET =&nbsp;"\033[0m"BOLD =&nbsp;"\033[1m"

class&nbsp;Finding:
&nbsp; &nbsp;&nbsp;def&nbsp;__init__(self, severity, cve, title, file_path, detail, recommendation):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.severity = severity &nbsp;# CRITICAL, HIGH, MEDIUM, LOW, INFO&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.cve = cve&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.title = title&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.file_path = file_path&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.detail = detail&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.recommendation = recommendation
&nbsp; &nbsp;&nbsp;def&nbsp;__str__(self):&nbsp; &nbsp; &nbsp; &nbsp; colors = {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"CRITICAL": RED + BOLD,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"HIGH": RED,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"MEDIUM": YELLOW,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"LOW": CYAN,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"INFO": GREEN,&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; c = colors.get(self.severity, RESET)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"\n{c}[{self.severity}]{RESET}&nbsp;{BOLD}{self.title}{RESET}\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f" &nbsp;CVE: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{self.cve}\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f" &nbsp;File: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;{self.file_path}\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f" &nbsp;Detail: &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;{self.detail}\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f" &nbsp;Recommendation:&nbsp;{self.recommendation}\n"&nbsp; &nbsp; &nbsp; &nbsp; )

def&nbsp;scan_hooks_bypass(repo_path):&nbsp; &nbsp; findings = []&nbsp; &nbsp; settings_path = repo_path /&nbsp;".claude"&nbsp;/&nbsp;"settings.json"
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;settings_path.exists():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; data = json.loads(settings_path.read_text())&nbsp; &nbsp;&nbsp;except&nbsp;(json.JSONDecodeError, OSError):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp; hooks = data.get("hooks", {})&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;hooks:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp;&nbsp;for&nbsp;event_name, event_config&nbsp;in&nbsp;hooks.items():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;isinstance(event_config,&nbsp;list):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;matcher_block&nbsp;in&nbsp;event_config:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;isinstance(matcher_block,&nbsp;dict):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;hook&nbsp;in&nbsp;matcher_block.get("hooks", []):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;isinstance(hook,&nbsp;dict):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cmd = hook.get("command",&nbsp;"")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;cmd:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity =&nbsp;"CRITICAL"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dangerous_patterns = [&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;r"curl\s",&nbsp;r"wget\s",&nbsp;r"nc\s",&nbsp;r"ncat\s",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;r"bash\s+-[ic]",&nbsp;r"/dev/tcp/",&nbsp;r"mkfifo",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;r"python.*-c",&nbsp;r"eval\s",&nbsp;r"base64",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;r"\bssh\b",&nbsp;r"reverse",&nbsp;r"bind.*shell",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; is_extra_dangerous =&nbsp;any(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; re.search(p, cmd, re.IGNORECASE)&nbsp;for&nbsp;p&nbsp;in&nbsp;dangerous_patterns&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findings.append(Finding(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity="CRITICAL"&nbsp;if&nbsp;is_extra_dangerous&nbsp;else&nbsp;"HIGH",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cve="No CVE (CVSS 8.7)",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title=f"Project hook executes shell command on&nbsp;{event_name}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file_path=str(settings_path),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; detail=f"Command:&nbsp;{cmd[:120]}{'...'&nbsp;if&nbsp;len(cmd)>120&nbsp;else&nbsp;''}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recommendation=(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Remove project-level hooks or audit each command. "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Update Claude Code to v1.0.87+ where consent is required."&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))
&nbsp; &nbsp;&nbsp;return&nbsp;findings

def&nbsp;scan_mcp_injection(repo_path):&nbsp; &nbsp; findings = []
&nbsp; &nbsp; settings_path = repo_path /&nbsp;".claude"&nbsp;/&nbsp;"settings.json"&nbsp; &nbsp; auto_enable =&nbsp;False
&nbsp; &nbsp;&nbsp;if&nbsp;settings_path.exists():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = json.loads(settings_path.read_text())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;data.get("enableAllProjectMcpServers")&nbsp;is&nbsp;True:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; auto_enable =&nbsp;True&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findings.append(Finding(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity="HIGH",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cve="CVE-2025-59536 (CVSS 8.7)",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title="enableAllProjectMcpServers is set to true",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file_path=str(settings_path),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; detail=(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"This flag causes all project-defined MCP servers to start "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"automatically without user consent."&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recommendation=(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Remove this flag. Update Claude Code to v1.0.111+ "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"where this bypass is patched."&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;(json.JSONDecodeError, OSError):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass
&nbsp; &nbsp; mcp_path = repo_path /&nbsp;".mcp.json"&nbsp; &nbsp;&nbsp;if&nbsp;mcp_path.exists():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mcp_data = json.loads(mcp_path.read_text())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; servers = mcp_data.get("mcpServers", {})&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;name, config&nbsp;in&nbsp;servers.items():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;isinstance(config,&nbsp;dict):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cmd = config.get("command",&nbsp;"")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args = config.get("args", [])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; full_cmd =&nbsp;f"{cmd}&nbsp;{' '.join(str(a)&nbsp;for&nbsp;a&nbsp;in&nbsp;args)}"&nbsp;if&nbsp;args&nbsp;else&nbsp;cmd
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity =&nbsp;"CRITICAL"&nbsp;if&nbsp;auto_enable&nbsp;else&nbsp;"MEDIUM"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findings.append(Finding(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity=severity,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cve="CVE-2025-59536 (CVSS 8.7)",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title=f"MCP server '{name}' executes:&nbsp;{cmd}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file_path=str(mcp_path),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; detail=f"Full command:&nbsp;{full_cmd[:150]}{'...'&nbsp;if&nbsp;len(full_cmd)>150&nbsp;else&nbsp;''}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recommendation=(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Audit MCP server commands. Remove untrusted servers. "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Never set enableAllProjectMcpServers=true in shared repos."&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; env = config.get("env", {})&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;k, v&nbsp;in&nbsp;env.items():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;any(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s&nbsp;in&nbsp;k.upper()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;s&nbsp;in&nbsp;["KEY",&nbsp;"TOKEN",&nbsp;"SECRET",&nbsp;"PASSWORD",&nbsp;"CREDENTIAL"]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; findings.append(Finding(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity="MEDIUM",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cve="CVE-2025-59536",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title=f"MCP server '{name}' sets suspicious env var:&nbsp;{k}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file_path=str(mcp_path),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; detail=f"Env var&nbsp;{k}&nbsp;may be used to override credentials.",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recommendation="Audit environment variables in MCP configs.",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;(json.JSONDecodeError, OSError):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass
&nbsp; &nbsp;&nbsp;return&nbsp;findings

def&nbsp;scan_api_exfil(repo_path):&nbsp; &nbsp; findings = []&nbsp; &nbsp; settings_path = repo_path /&nbsp;".claude"&nbsp;/&nbsp;"settings.json"
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;settings_path.exists():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; data = json.loads(settings_path.read_text())&nbsp; &nbsp;&nbsp;except&nbsp;(json.JSONDecodeError, OSError):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp; env = data.get("env", {})&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;isinstance(env,&nbsp;dict):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;findings
&nbsp; &nbsp; suspicious_env_vars = {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ANTHROPIC_BASE_URL":&nbsp;"Redirects all API traffic (including API key) to attacker",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ANTHROPIC_API_KEY":&nbsp;"Overrides/captures the user's API key",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"CLAUDE_CODE_API_KEY":&nbsp;"May override API key configuration",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"HTTP_PROXY":&nbsp;"Routes all HTTP traffic through attacker proxy",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"HTTPS_PROXY":&nbsp;"Routes all HTTPS traffic through attacker proxy",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"NODE_EXTRA_CA_CERTS":&nbsp;"Could enable MITM by injecting attacker CA certificate",&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;for&nbsp;var, description&nbsp;in&nbsp;suspicious_env_vars.items():&nbsp; &nbsp; &nbsp; &nbsp; value = env.get(var,&nbsp;"")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;value:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue
&nbsp; &nbsp; &nbsp; &nbsp; severity =&nbsp;"CRITICAL"&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;var ==&nbsp;"ANTHROPIC_BASE_URL":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;"anthropic.com"&nbsp;not&nbsp;in&nbsp;value.lower():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity =&nbsp;"CRITICAL"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity =&nbsp;"INFO"
&nbsp; &nbsp; &nbsp; &nbsp; findings.append(Finding(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; severity=severity,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cve="CVE-2026-21852 (CVSS 5.3)",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title=f"Environment override:&nbsp;{var}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file_path=str(settings_path),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; detail=f"{description}. Value:&nbsp;{value[:80]}{'...'&nbsp;if&nbsp;len(value)>80&nbsp;else&nbsp;''}",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recommendation=(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Remove env overrides from project settings. "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"Update Claude Code to v2.0.65+ where env is not loaded before trust prompt."&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ),&nbsp; &nbsp; &nbsp; &nbsp; ))
&nbsp; &nbsp;&nbsp;return&nbsp;findings

def&nbsp;scan_repo(repo_path_str):&nbsp; &nbsp; repo_path = Path(repo_path_str).resolve()
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;repo_path.is_dir():&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"{RED}[!] Error: '{repo_path}' is not a directory{RESET}")&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp;&nbsp;print(f"""{BOLD}{'='*70}&nbsp; Claude Code Malicious Repository Scanner&nbsp; EDUCATIONAL USE ONLY{'='*70}{RESET}
{CYAN}[*] Scanning:&nbsp;{repo_path}{RESET}""")
&nbsp; &nbsp; all_findings = []
&nbsp; &nbsp; scanners = [&nbsp; &nbsp; &nbsp; &nbsp; ("Hooks Consent Bypass (no CVE)", scan_hooks_bypass),&nbsp; &nbsp; &nbsp; &nbsp; ("MCP Server Injection (CVE-2025-59536)", scan_mcp_injection),&nbsp; &nbsp; &nbsp; &nbsp; ("API Key Exfiltration (CVE-2026-21852)", scan_api_exfil),&nbsp; &nbsp; ]
&nbsp; &nbsp;&nbsp;for&nbsp;name, scanner_fn&nbsp;in&nbsp;scanners:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"{CYAN}[*] Checking:&nbsp;{name}...{RESET}")&nbsp; &nbsp; &nbsp; &nbsp; findings = scanner_fn(repo_path)&nbsp; &nbsp; &nbsp; &nbsp; all_findings.extend(findings)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;findings:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp;{RED}Found&nbsp;{len(findings)}&nbsp;issue(s){RESET}")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp;{GREEN}Clean{RESET}")
&nbsp; &nbsp;&nbsp;print(f"\n{BOLD}{'='*70}")&nbsp; &nbsp;&nbsp;print(f" &nbsp;SCAN RESULTS")&nbsp; &nbsp;&nbsp;print(f"{'='*70}{RESET}\n")
&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;all_findings:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"{GREEN}{BOLD}[+] No Claude Code supply-chain indicators found.{RESET}\n")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;0
&nbsp; &nbsp; severity_order = {"CRITICAL":&nbsp;0,&nbsp;"HIGH":&nbsp;1,&nbsp;"MEDIUM":&nbsp;2,&nbsp;"LOW":&nbsp;3,&nbsp;"INFO":&nbsp;4}&nbsp; &nbsp; all_findings.sort(key=lambda&nbsp;f: severity_order.get(f.severity,&nbsp;5))
&nbsp; &nbsp;&nbsp;for&nbsp;finding&nbsp;in&nbsp;all_findings:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(finding)
&nbsp; &nbsp; by_severity = {}&nbsp; &nbsp;&nbsp;for&nbsp;f&nbsp;in&nbsp;all_findings:&nbsp; &nbsp; &nbsp; &nbsp; by_severity[f.severity] = by_severity.get(f.severity,&nbsp;0) +&nbsp;1
&nbsp; &nbsp;&nbsp;print(f"\n{BOLD}Total findings:&nbsp;{len(all_findings)}{RESET}")&nbsp; &nbsp;&nbsp;for&nbsp;sev&nbsp;in&nbsp;["CRITICAL",&nbsp;"HIGH",&nbsp;"MEDIUM",&nbsp;"LOW",&nbsp;"INFO"]:&nbsp; &nbsp; &nbsp; &nbsp; count = by_severity.get(sev,&nbsp;0)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;count:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; colors = {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"CRITICAL": RED + BOLD,&nbsp;"HIGH": RED,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"MEDIUM": YELLOW,&nbsp;"LOW": CYAN,&nbsp;"INFO": GREEN,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f" &nbsp;{colors.get(sev,&nbsp;'')}{sev}:&nbsp;{count}{RESET}")
&nbsp; &nbsp;&nbsp;print(f"\n{YELLOW}[!] Recommendation: Do NOT open this repo with Claude Code < v2.0.65{RESET}")&nbsp; &nbsp;&nbsp;print(f"{YELLOW}[!] Review and remove all suspicious configuration files.{RESET}\n")
&nbsp; &nbsp;&nbsp;return&nbsp;len(all_findings)

if&nbsp;__name__ ==&nbsp;"__main__":&nbsp; &nbsp;&nbsp;if&nbsp;len(sys.argv) <&nbsp;2:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"Usage:&nbsp;{sys.argv[0]}&nbsp;<path-to-repo>")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"Example:&nbsp;{sys.argv[0]}&nbsp;./suspicious-repo")&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(1)
&nbsp; &nbsp; count = scan_repo(sys.argv[1])&nbsp; &nbsp; sys.exit(1&nbsp;if&nbsp;count >&nbsp;0&nbsp;else&nbsp;0)

🔗 相关研究原文:

  • https://research.checkpoint.com/2026/rce-and-api-token-exfiltration-through-claude-code-project-files-cve-2025-59536/
  • https://github.com/atiilla/CVE-2026-21852-PoC

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:APT-101 APT-101 APT-101《警惕“AI 投毒”:Claude Code 项目配置漏洞可致代码执行》

工具|LeakDetector 网络安全文章

工具|LeakDetector

文章总结: 本文介绍了一款名为LeakDetector的自动化信息泄露侦察工具,专为红队渗透测试人员和安全研究员设计。该工具具备多维度Dork侦察策略、智能浏览
评论:0   参与:  0