上一篇從 MCP Server 出發,看 Tool Poisoning 與傳統 API 漏洞,這篇換到 Client。
Client 要跟 Server 連線、在本機把 Server 跑起來、處理授權流程,還保管著「哪些 Server 已經獲准」的名單,那份名單如果被改掉,電腦可能直接跑起攻擊者指定的程式。
MCPoison:名字沒變,但命令已經換了#
Check Point 公開的 CVE-2025-54136 又稱 MCPoison,研究指出 Cursor 在 1.3 之前的版本把 MCP 的核准狀態綁在 Server 名稱上,只要名稱沒變,Client 就當作使用者已經同意過了,不會再問一次,1.3 之後才改成設定動過就要重新核准。
Cursor 的 MCP 設定放在專案裡的 .cursor/rules/mcp.json,會跟著程式碼一起進版控,攻擊者可以先在共用的 Repository 提交無害的 MCP 設定,等某位同事把專案拉下來、在 Cursor 裡按下允許,核准一旦拿到手,再把同一個名稱底下的命令與參數換掉就好,往後每次打開專案,跑的都是新的那條命令。

這個「先拿到信任再換內容」的手法,AI 黑魔法(17) 的 Rug Pull 也用過,換的位置不一樣而已。
| 比較項目 | Rug Pull | MCPoison |
|---|---|---|
| 誰換掉東西 | 惡意 MCP Server | 能改 repo 的人 |
| 換掉什麼 | Tool Description、工具清單 | mcp.json 裡的啟動命令與參數 |
| 誰被騙 | 模型,它照著新的描述做事 | Client,它以為名稱沒變就是同一個東西 |
| 後果 | 模型走錯流程、參數被塞東西 | 電腦直接執行另一條命令 |
CurXecute:等你按下拒絕,一切都已經太遲了#
MCPoison 要有人有權限去改 repo,CurXecute 連這個門檻都省了,動手改設定的是 Agent 自己,而達成這個攻擊的需求有兩項:
mcp.json就放在工作區裡,前面說過它決定 Client 啟動哪些 Server、用什麼命令啟動。- 根據 CVE-2025-54135,Cursor 在 1.3.9 之前的版本,寫入工作區裡的檔案不需要使用者核准,dotfile 只有改既有的要問過,新建一個不用。
攻擊流程如下:
- 攻擊者在 Slack 的公開頻道留下一段間接注入的內容,看起來就是一則普通訊息。
- 使用者請 Cursor 幫忙摘要那個頻道,這段內容就跟著被讀進 Context。
- 注入的內容誘導 Agent 去「改善」工作區裡的
mcp.json。 - 寫檔案這一步不需要問過使用者,新的那條命令就這樣落進設定裡,被 Client 啟動起來。
- 確認視窗這時候才跳出來,使用者就算按下拒絕,也只是拒絕一件已經做完的事。
MCPoison 錯在把核准綁在名稱上,CurXecute 錯在核准來得比動作晚,讓「你要允許嗎」這道關卡失去了意義。
惡意 Server 反過來打 Client#
JFrog 發現的 CVE-2025-6514 裡,惡意 Server 在 OAuth 流程裡回傳動過手腳的 authorization_endpoint,Client 要開瀏覽器的時候把那串網址交給了 shell,在 Windows 上變成完整的 PowerShell 命令注入,macOS 跟 Linux 則是能執行任意執行檔、但參數控制有限。這裡沒有任何模型被說服,純粹是輸入驗證跟開瀏覽器那段流程沒做好。
官方的除錯工具 MCP Inspector 自己也有洞(Oligo 公開的 CVE-2025-49596),它曾經預設沒有認證,攻擊者結合 DNS Rebinding 與 CSRF,使用者開到惡意網頁,就會在自己的電腦上跑起攻擊者的命令。
mcp-remote 跟 MCP Inspector 都是跑在自己電腦上的東西,只監聽本機不會讓它們自動安全,只要能跑起別的程式,就要當成對外服務來保護。
對話裡的 UI 也不是自動可信#
聊天視窗裡跳出一張表單,你選了時段、按下確認,這張表單不是 Client 畫的,MCP Apps 讓 Server 用 ui:// Resource 送一份 HTML 過來,Host 再把它顯示在對話裡,長得跟 Client 自己的介面一樣,可是要連去哪個網域、要不要拿相機跟麥克風,寫在那份 HTML 裡的都是 Server。

MCP Apps 2026-01-26 規格要求 Host 把那份 HTML 關進 sandboxed iframe,再照 Server 事先報上來的清單設 CSP,它只連得到清單上的網址,相機跟麥克風也要在清單裡才拿得到。Server 如果後來換掉那份 HTML、把 CSP 放寬、多要一個權限,規格都沒說要不要再問過使用者,畫面上顯示的已經不是當初核准的那一份,這是 MCPoison 那個問題換到 UI 這一層。
真的要做高風險的操作,Host 得自己把 Server 身分跟實際參數顯示出來,不能讓 Resource 裡的按鈕直接跳過確認。
風險回到那支程式本身#
Client 會在你的電腦上把 MCP Server 啟動起來,它跑在你的帳號底下,能碰到的東西跟你自己差不多,所以裝之前該做的事跟裝別的軟體一樣,先弄清楚它是哪裡來的、跑起來碰得到哪些東西。
stdio 這種傳輸方式,Client 是直接在你電腦上把 Server 跑起來的,走本機的只有他們兩邊往來的訊息,但那支 Server 自己照樣連得到網路,也讀得到你的檔案,因此它應該用權限最小的帳號跑,只讓它讀得到真的要用的那幾個目錄,能設成唯讀就設唯讀,給它的 Credential 也用專用的、時效短的,這樣就算哪天它被換掉,能拿走的東西也有限。
MCP Client 的防線要放在哪裡#
MCP 官方 Security Best Practices 對 Client 有幾項要求:

- 啟動本機 Server 之前,把要執行的完整命令跟參數原封不動顯示出來,不能截斷,讓使用者看過再決定,能丟進沙箱就丟進沙箱。
- Server 給的 Authorization URL 只接受
https,javascript:、data:、file:這些一律擋掉,而且不要把網址交給 shell 去開,前面那個 mcp-remote 的 RCE 就是這裡沒擋住。 - 去抓 OAuth Metadata 的時候要防 SSRF,私有網段、Loopback 跟
169.254.169.254這種雲端 Metadata 位址都要擋,免得 Server 指到哪 Client 就打到哪。 - Scope 一開始只拿最低限度的讀取權限,真的要做高風險的操作,再單獨去申請那一項。
規格沒寫的是第五件事,東西改掉之後要不要再問過使用者一次。MCPoison 是命令換了、名字沒換,Client 就不再問;MCP Apps 是那份 HTML 換了一版,Host 一樣沒問。官方那份文件從頭到尾沒有一節在講這件事,所以命令、網址、工具描述只要有一項跟當初核准的不一樣,那份同意就該由 Client 自己重新拿一次。
同一份設定檔還有另一個常見問題,長效 Token 被硬編碼在裡面(OWASP MCP01:2025)。它決定哪些程式會被跑起來、哪一組 Credential 給誰用,該用管程式碼的標準去管,不是使用者偏好那種等級的東西。
這篇的小總結#
你有打開過自己電腦上那份 MCP 設定檔,看裡面到底核准了哪些 Server 嗎?當初按下允許之後,有沒有再回頭看它們現在跑的是哪條命令、拿得到哪些權限?設定改掉要不要重新問過你?規格裡沒有寫,那清單換了內容 Client 不一定會讓你知道。
下一篇上靶場,用 Lakera Agent Breaker 看一段工具描述怎麼讓天氣查詢把使用者的聊天記錄一起送出去。