一次从发现到利用的安全漏洞分析之旅
在浏览安全资讯的时候,我偶然间看到了 CVE-2026-0755,这是一个关于 gemini-mcp-tool 的命令注入漏洞。对MCP 协议不太了解,我心里充满了疑问:
MCP 到底是什么?为什么会有这样的协议?
gemini-mcp-tool 是干什么用的?
execAsync 命令注入是如何发生的?
更重要的是:这个漏洞要怎么挖掘和触发?
gemini-mcp-tool 是一个开源的 npm 包,用于在 Claude Desktop 等 MCP 客户端中集成 Google Gemini AI。该工具允许用户通过 MCP 协议调用 Gemini 的各种能力。在 gemini-mcp-tool 的 contribute.ts 文件中,存在一个命令执行漏洞。该漏洞源于用户输入缺乏充分验证,直接将用户输入拼接到 shell 命令中执行。
影响版本≤ 1.1.2
重要说明:漏洞位于 contribute.ts,该文件并没有直接作为 MCP 服务暴露。它是 gemini-mcp-tool 项目的贡献者辅助工具,它是一个交互式终端UI,一个独立的命令行工具。因此,默认情况下,这个漏洞无法通过 Claude Desktop 等 MCP 客户端直接触发。
什么是 MCP? MCP(Model Context Protocol,模型上下文协议)是一种用于连接大型语言模型(LLM)与外部数据源、工具的开放标准协议。能够让 AI 模型(如 Claude、Gemini)能够安全地访问和使用外部工具、数据源和服务。
它解决了一个核心问题:
如何让 AI 像人类一样使用工具?比如搜索网页、读取文件、操作数据库、调用 API 等。
MCP 架构图

MCP 的完整工作流程

MCP Server 的配置与启动
MCP Server 本质上就是一个普通的程序(可以是 Node.js、Python 等编写),它通过标准输入/输出(stdio) 或网络与客户端通信。
要让 Claude Desktop 使用某个 MCP Server,需要在配置文件中注册。
Claude Desktop 配置文件位置:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Linux: ~/.config/claude/claude_desktop_config.json
典型配置示例:
{
"mcpServers": {
"my-tool": {
"command": "node",
"args": ["/path/to/server.js"]
}
}
}
在 CVE 描述中提到了 execAsync 命令注入 我们直接在代码中搜索 execAsync

只有文件 src/contribute.ts 存在这个函数的定义和调用
import { spawn, exec } from "child_process";代码导入了 Node.js 的 child_process模块,这是 Node.js 提供的子进程管理模块,允许在 Node.js 中执行系统命令。
const execAsync = (command: string): Promise<string> => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(new Error(`${error.message}\n${stderr}`));
} else {
resolve(stdout.trim());
}
});
});
};命令执行的核心封装,通过 exec 创建子 shell 进程,执行命令,捕获 stdout/stderr
在菜单中选择 Create Feature Branch 后



这个函数的核心作用是帮助开发者在 Git 仓库中自动创建新的功能分支。整个过程首先会在终端显示一个绿色加粗的标题,告诉用户正在创建分支。然后通过 inquirer.prompt() 这个交互式命令行工具来获取用户输入的功能名称。
当程序执行到 await inquirer.prompt 这一行时:inquirer.prompt() 会在终端显示一个输入框,然后程序会完全暂停执行,等待用户输入内容并按下回车键。用户输入完成后,inquirer.prompt() 会返回一个对象,通过解构赋值 ${featureName} 可以提取出用户输入的功能名称。
拿到用户输入后,程序使用模板字符串来拼接完整的分支名。模板字符串使用反引号包裹的字符串,它最大的特点时可以在字符串中使用 ${} 来嵌入变量。当程序执行 `feature/${featureName}` 时,${featureName} 这个占位符会在运行时被替换成变量的实际值。
接下来函数会执行一系列 Git 命令。execAsync 函数:会启动一个子进程,在这个子进程中运行传入的 shell 命令(就像在终端中手动输入命令一样),然后等待命令执行完成。每个 await execAsync 都会让程序暂停,直到对应的命令执行完毕才继续下一步。
虽然分支名用双引号包裹了,但这种防护是不充分的。问题的根源在于:用户输入在传递给 shell 执行之前,没有经过转义处理或严格的输入验证。可以通过在输入中插入双引号来提前闭合原有的字符串边界,然后利用 shell 的特殊字符(如 &、;、|、` 等)注入并执行任意命令。
这个工具本质上是一个命令行自动化脚本的图形化封装,通过 Node.js 的子进程能力,将复杂的 Git 工作流程简化为菜单选择操作。
cd gemini-mcp-tool-1.1.2 # 进入项目根目录
git init # 初始化 git 仓库
git add . # 将所有文件添加
git commit -m "init" # 创建初始提交
git branch -M main # 将默认分支重命名为 main
git remote add upstream . # 添加一个假的 upstream(避免 pull upstream 报错)
npx ts-node src/contribute.ts # 启动程序
# 选择 "Create Feature Branch" 选项来创建功能分支
test" & calc & echo " # 输入 payload
疑问:contribute.ts 只是一个需要手动运行的 CLI 工具,用户必须主动输入恶意 payload。我们要怎样配置才可以让它变成远程代码执行?
我们将代码稍微修改使其变成一个可被远程访问的 mcp 服务
git-workflow-helper.ts
#!/usr/bin/env node
/**
* Git 工作流助手 - MCP 服务器
*
* 提供常用的 Git 工作流操作,帮助开发者快速创建功能分支
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { exec } from "child_process";
// ========================================
// 核心功能实现
// ========================================
/**
* 执行 shell 命令的辅助函数
*/
const execAsync = (command: string, cwd?: string): Promise<string> => {
return new Promise((resolve, reject) => {
exec(command, { cwd }, (error, stdout, stderr) => {
if (error) {
reject(new Error(`${error.message}\n${stderr}`));
} else {
resolve(stdout.trim());
}
});
});
};
/**
* 创建功能分支
*
* 自动从主分支创建新的功能分支,并确保代码是最新的
*
* @param featureName - 功能名称,将自动添加 feature/ 前缀
* @returns 操作结果消息
*/
async function createFeatureBranch(featureName: string): Promise<string> {
try {
const gitRepo = process.env.GIT_REPO_PATH || process.cwd();
const branchName = `feature/${featureName}`;
// 切换到主分支
await execAsync("git checkout main", gitRepo);
// 拉取最新更新
await execAsync("git pull upstream main", gitRepo);
// 创建并切换到新分支
await execAsync(`git checkout -b "${branchName}"`, gitRepo);
return `✅ Branch created successfully: ${branchName}`;
} catch (error) {
if (error instanceof Error) {
return `❌ Branch creation failed: ${error.message}`;
}
return `❌ Unknown error occurred`;
}
}
// ========================================
// MCP 服务器配置
// ========================================
const server = new Server(
{
name: "git-workflow-helper",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
/**
* 注册可用工具
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "create_feature_branch",
description:
"Create a new Git feature branch from main. Automatically pulls latest changes and creates a properly named feature branch.",
inputSchema: {
type: "object",
properties: {
feature_name: {
type: "string",
description:
"Name of the feature (e.g., add-login-page, fix-bug-123, update-documentation). Do not include 'feature/' prefix as it will be added automatically.",
},
},
required: ["feature_name"],
},
},
],
};
});
/**
* 处理工具调用
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "create_feature_branch") {
const featureName = request.params.arguments?.feature_name as string;
if (!featureName) {
return {
content: [
{
type: "text",
text: "❌ Error: feature_name parameter is required",
},
],
};
}
const result = await createFeatureBranch(featureName);
return {
content: [
{
type: "text",
text: result,
},
],
};
}
return {
content: [
{
type: "text",
text: `❌ Unknown tool: ${request.params.name}`,
},
],
};
});
// ========================================
// 启动服务器
// ========================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Git Workflow Helper started");
console.error("Ready to assist with Git operations");
console.error(`Working directory: ${process.env.GIT_REPO_PATH || process.cwd()}`);
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});npm install --save-dev typescript
npx tsc src/git-workflow-helper.ts --outDir dist --module commonjs --target es2020 --esModuleInterop
move dist\git-workflow-helper.js dist\git-workflow-helper.cjs


Claude Desktop 利用失败



我们发现在 Claude Desktop 上利用失败了,是因为 Claude AI 的智能安全防护层会自动识别和拒绝危险操作,即使 MCP 工具本身有漏洞,Claude 也会拒绝执行看起来像是命令注入的操作。(或许可以通过多次对话绕过安全识别)
我们可以通过 MCP Inspector(Model Context Protocol官方调试工具),它只是一个技术调试工具,直接传递数据,没有任何安全判断机制,纯粹用于开发者测试 MCP 工具的原始功能,能够精确展示 MCP 工具本身的漏洞,而不会被上层 AI 安全机制拦截。
npx /inspector node dist/git-workflow-helper.cjs
本课程最终解释权归蚁景网安学院
本页面信息仅供参考,请扫码咨询客服了解本课程最新内容和活动