博客 / 詳情

返回

gemini-mcp-tool 命令注入漏洞深度分析(CVE-2026-0755)

一次從發現到利用的安全漏洞分析之旅

在瀏覽安全資訊的時候,我偶然間看到了 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 架構圖

image

MCP 的完整工作流程

image

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"]
    }
  }
}
001.png
關鍵點: 當 Claude Desktop 啓動時,它會讀取這個配置,並自動在本地啓動這些 MCP Server 程序。

漏洞復現&分析

在 CVE 描述中提到了 execAsync 命令注入 我們直接在代碼中搜索 execAsync

image

只有文件 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 後

image

image

image

這個函數的核心作用是幫助開發者在 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

7

‍疑問: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

image

image

image

Claude Desktop 利用失敗

image

image

image

我們發現在 Claude Desktop 上利用失敗了,是因為 Claude AI 的智能安全防護層會自動識別和拒絕危險操作,即使 MCP 工具本身有漏洞,Claude 也會拒絕執行看起來像是命令注入的操作。(或許可以通過多次對話繞過安全識別)

我們可以通過 MCP Inspector(Model Context Protocol官方調試工具),它只是一個技術調試工具,直接傳遞數據,沒有任何安全判斷機制,純粹用於開發者測試 MCP 工具的原始功能,能夠精確展示 MCP 工具本身的漏洞,而不會被上層 AI 安全機制攔截。

npx @modelcontextprotocol/inspector node dist/git-workflow-helper.cjs

8

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

  

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.