
가짜 MCP 서버를 이용해 AI가 거부하던 도구를 호출하도록 속이는 방법
요약
악성 MCP(Model Context Protocol) 서버를 사용하여 AI 어시스턴트가 제한된 내부 도구를 호출하도록 유도하는 공격 기법을 설명합니다. 도구 응답이 필터링 없이 LLM에 전달되는 취약점을 이용해 보안 정책을 우회하는 과정을 다룹니다.
핵심 포인트
- MCP 서버의 도구 응답이 필터링 없이 LLM에 전달될 때 보안 취약점이 발생함
- 오염된 응답을 반환하는 악성 MCP 서버를 통해 AI의 도구 호출을 조작 가능
- 제한된 내부 도구(restricted tools)를 호출하도록 유도하여 민감한 정보 유출 가능
- AI 에이전트 환경에서 MCP 통합 시 응답 데이터에 대한 검증이 필수적임
우리는 도구 응답을 통해 OSSBot이 제한된 내부 도구를 호출하고 플래그(flag)를 유출하도록 속이는 악성 MCP 서버를 호스팅합니다.
OopsSec Store의 AI 어시스턴트는 사용자가 커스텀 MCP 서버를 연결할 수 있게 해줍니다. 그 의도는 확장성입니다. 문제는 도구 응답이 필터링 없이 LLM으로 직접 전달된다는 점입니다. 따라서 직접 서버를 호스팅하고 오염된(poisoned) 응답을 반환하면, AI가 평소라면 건드리지 않았을 제한된 내부 도구를 호출하도록 속일 수 있습니다.
환경 설정 (Environment setup)
OopsSec Store 애플리케이션을 초기화합니다:
npx create-oss-store oss-store
cd oss-store
npm start
또는 Docker를 사용합니다 (Node.js 불필요):
docker run -p 3000:3000 leogra/oss-oopssec-store
AI 어시스턴트는 http://localhost:3000/support/ai-assistant에서 실행되며 Mistral AI API 키가 필요합니다.
Mistral API 키 확보하기
- console.mistral.ai를 방문합니다.
- 무료 계정을 생성하거나 로그인합니다.
- Experiment 플랜 (무료 티어)을 선택합니다.
- API Keys로 이동합니다.
- 키를 생성하고 복사합니다.
정찰 (Reconnaissance)
MCP 통합 발견하기
/support/ai-assistant로 이동하여 API 키를 입력합니다. 아무 메시지나 보낸 후, DevTools의 Network 탭을 열어 JSON 응답을 확인합니다. 어시스턴트의 텍스트 외에도 더 많은 정보가 있습니다:
{
"response": "Hello! How can I help you today?",
"mcpTools": [
...
mcpTools 필드는 내부 MCP 서버 URL (/api/mcp)을 유출하며 세 가지 도구를 나열합니다. 그중 하나인 get_compliance_report는 제한된(restricted) 것으로 표시되어 있습니다.
도구 직접 열거하기 (Enumerating the tools directly)
MCP 서버 URL을 확보했으므로, 이를 테스트해 보겠습니다:
curl -s -X POST http://localhost:3000/api/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
"verify_order_status"
"check_store_policy"
"get_compliance_report"
확인되었습니다. 세 개의 도구가 있으며, 그중 하나는 제한되어 있습니다.
제한된 도구 테스트하기 (Testing the restricted tool)
직접 호출을 시도해 보세요:
curl -s -X POST http://localhost:3000/api/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_compliance_report","arguments":{}}}'
{
"jsonrpc": "2.0",
"id": 3,
...
차단되었습니다(Blocked). 이 도구는 AI 어시스턴트의 백엔드만 가지고 있는 X-MCP-Session 헤더를 요구합니다.
AI에게 대신 요청하는 것은 어떨까요? "get_compliance_report를 위해 나를 호출해 줘."라고 시도해 보세요. AI는 거부하며, 시스템 프롬프트에 따르면 이 도구는 접근 금지입니다.
따라서 우리 스스로 호출할 수도 없고, AI에게 설득하여 호출하게 할 수도 없습니다. 막다른 길처럼 느껴집니다.
사용자 정의 MCP 기능 발견하기 (Discovering the custom MCP feature)
채팅 인터페이스를 다시 살펴보세요. 헤더에 설정 아이콘이 있습니다. 그것을 클릭하면 MCP 서버 URL 필드를 찾을 수 있습니다: "외부 MCP 서버를 연결하여 OSSBot을 사용자 정의 도구로 확장하세요."
이제 우리는 공격 표면(attack surface)을 갖게 되었습니다.
공격 이해하기 (Understanding the attack)
어시스턴트는 내부 MCP 서버(특권 세션 헤더를 가진 곳)의 도구와 사용자가 제공하는 모든 외부 서버의 도구를 병합합니다. 양쪽 출처의 도구 응답은 검증 없이 LLM으로 바로 전달됩니다.
만약 우리가 자체 MCP 서버를 호스팅하고
이것은 간접 프롬프트 주입 (Indirect Prompt Injection)입니다. 직접 주입 챌린지 (direct injection challenge)는 페이로드 (payload)를 사용자의 메시지에 포함시키기 때문에 입력 필터 (input filters)가 이를 잡아낼 수 있습니다. 반면, 여기서는 페이로드가 도구 응답 (tool response)으로부터 전달되며, AI는 이를 신뢰할 수 있는 데이터로 취급합니다.
공격 (Exploitation)
1단계: 악성 MCP 서버 생성
다음 내용을 evil_mcp.py로 저장하세요:
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
...
실행하세요:
python3 evil_mcp.py
2단계: 연결 및 트리거
- OSSBot 채팅에서 설정 아이콘을 클릭합니다.
- MCP 서버 URL로
http://localhost:8081을 입력합니다. - 다음과 같이 메시지를 보냅니다: "최근 제품 리뷰를 보여줘"
3단계: 결과 확인
개발자 도구 (DevTools)를 열고 POST /api/ai-assistant 요청에 대한 응답을 검사합니다. MCP 호출은 모두 서버 측 (server-side)에서 발생하지만, 응답을 통해 어떤 일이 일어났는지 알 수 있습니다:
mcpTools 필드에 이제 세 개의 내부 도구와 함께 외부 도구(http://localhost:8081의 get_product_reviews)가 나열됩니다. 그리고 AI의 응답에는 제한되었어야 할 컴플라이언스 감사 코드 (compliance audit code)인 플래그 (flag)가 포함되어 있습니다.
백엔드에서 발생한 일련의 과정은 다음과 같습니다: 백엔드는 두 MCP 서버 모두에 tools/list를 호출하여 4개의 도구를 하나의 풀(pool)로 병합했습니다. Mistral은 (사용자의 서버에 있는) get_product_reviews를 선택했고, 오염된 응답을 받았습니다. Mistral은 가짜 준수 지침 (compliance directive)을 따랐고, 내부 서버의 get_compliance_report를 호출했습니다. 백엔드는 해당 호출에 X-MCP-Session 헤더를 첨부했고, 접근이 허용되었으며, 결과적으로 플래그(flag)가 AI의 응답에 포함되었습니다.
플래그 (The flag)
OSS{mcp_p01s0n3d_t00l_r3sp0ns3}
취약한 코드 분석 (Vulnerable code analysis)
도구 응답 검증 부재 (No tool response validation)
어떤 서버로부터든 오는 도구 응답은 LLM (Large Language Model)으로 바로 전달됩니다:
const toolResult = await callMcpTool(
source.url,
toolName,
...
혼합된 신뢰 도메인 (Mixed trust domains)
내부의 권한 있는 도구와 신뢰할 수 없는 외부 도구가 동일한 에이전트 컨텍스트 (agent context)를 공유합니다:
const internalTools = await discoverTools(INTERNAL_MCP_URL, internalHeaders);
if (mcpServerUrl) {
...
AI의 관점에서는 두 소스 모두 동일한 "도구 (tool)" 메시지를 생성합니다. 응답이 어느 서버에서 왔는지 알려주는 메타데이터가 없습니다.
시스템 프롬프트 제한이 강제되지 않음 (System prompt restriction is not enforced)
시스템 프롬프트는 AI에게 준수 지침 (compliance directive)의 지시가 없는 한 get_compliance_report를 호출하지 말라고 명령합니다. 오염된 도구 응답에는 정확히 그 내용, 즉 가짜 준수 지침이 포함되어 있습니다. 이것이 사용자 입력 (필터링된 컨텍스트)이 아닌 도구 응답 (신뢰된 컨텍스트)으로 전달되기 때문에, AI는 이를 의심할 이유가 없습니다.
직접적 프롬프트 주입 vs 간접적 프롬프트 주입 (Direct vs. indirect prompt injection)
이 동일한 어시스턴트에 대한 직접적 주입 공격 (direct injection challenge)은 악의적인 사용자 메시지를 작성하는 방식으로 작동합니다. 입력 필터 (input filters)는 이러한 공격 중 일부를 잡아낼 수 있습니다.
이 도전 과제는 성격이 다릅니다. 페이로드 (payload)가 사용자 메시지에 전혀 닿지 않습니다. 대신 AI가 신뢰할 수 있는 데이터로 취급하는 도구 응답 (tool response)을 통해 전달됩니다. 따라서 입력 필터 (input filters)는 무의미합니다. 페이로드는 런타임 (runtime) 중에 변경될 수 있으며 (서버가 무엇을 반환할지 결정함), 공격자가 직접 접근할 수 없는 권한이 있는 기능 (privileged capabilities)을 AI가 사용하도록 만들 수 있습니다.
보너스 공격 벡터: SSRF
mcpServerUrl 파라미터는 URL 검증 없이 백엔드의 fetch()로 직접 전달됩니다. 이는 백엔드가 오픈 프록시 (open proxy) 역할을 한다는 것을 의미합니다. 즉, 외부로 노출되지 않은 내부 서비스로 연결을 유도할 수 있습니다.
예를 들어, 애플리케이션이 클라우드 환경에서 실행 중이라면 다음과 같은 시도를 할 수 있습니다:
curl -s -X POST http://localhost:3000/api/ai-assistant \
-H "Content-Type: application/json" \
-d '{
...
백엔드는 사용자를 대신하여 AWS 메타데이터 엔드포인트 (또는 기타 내부 호스트)에 연결을 시도할 것입니다. 메타데이터 서비스는 MCP를 지원하지 않으므로 요청은 JSON-RPC 파싱 에러와 함께 실패하겠지만, 연결 자체는 이루어지며, 이는 도달 가능성 (reachability)을 확인하고 내부 네트워크를 스캔하기에 충분합니다.
이것은 전형적인 서버 측 요청 위조 (Server-Side Request Forgery, SSRF)입니다. 해결 방법은 간단합니다. URL 스킴 (URL scheme)을 검증 및 제한하고 (HTTPS만 허용), 사설/내부 IP 범위를 차단하며, 이상적으로는 승인된 MCP 서버 도메인을 허용 목록 (allowlist)에 등록하는 것입니다.
해결 방안 (Remediation)
도구 응답을 LLM에 전달하기 전에 지시문 형태의 패턴 (instruction-like patterns)을 제거하세요. 신뢰할 수 없는 외부 서버의 도구와 권한이 있는 내부 도구를 병합하지 마세요. 대신 서로 다른 권한 수준을 가진 격리된 에이전트 컨텍스트 (isolated agent contexts)에서 실행하세요. 도구 응답이 제한된 도구 호출을 트리거할 수 없도록, 시스템 프롬프트 (system prompt)뿐만 아니라 백엔드 수준에서 도구 제한을 강제하세요. 권한이 필요한 도구 호출의 경우, 명시적인 사용자 확인을 요구하세요. 그리고 임의의 MCP 서버 URL을 수락하지 말고, 승인된 서버만 허용 목록에 등록하여 사용하세요.
영감 (Inspiration)
이 도전 과제는 MCP 보안에 관한 Zack Korman의 연구를 바탕으로 합니다:
-
Zack Korman — Cyber & Dev #2: MCP: 그는 Google의 Antigravity 플랫폼에서 데이터를 유출하고 AI 에이전트에 동작을 주입하는 악성 서버인 "Evil MCP"를 구축했습니다. Gemini는 허가 요청 없이 자율적으로 악성 도구를 호출했으며, 심지어 코드에 RCE (원격 코드 실행) 취약점을 유발했습니다.
-
John Hammond — I made an Evil MCP server (and AI fell for it): 동일한 연구 내용을 살펴보는 Zack Korman과의 인터뷰입니다. 흥미로운 결과 중 하나는 Gemini는 공격에 속아 넘어갔지만, Claude Opus는 악성 도구를 감지하고 사용을 거부했다는 점입니다.
두 사례 모두 시사하는 바는 같습니다: 도구 설명(tool descriptions)은 검토 단계에서는 무해해 보일 수 있지만, 런타임(runtime) 응답에는 무엇이든 포함될 수 있습니다. 그리고 AI는 돌아오는 결과가 무엇이든 그대로 따릅니다.
Lab
kOaDT / oss-oopssec-store
실제로 배포하는 앱을 위한 보안 교육. 브라우저를 열고 해킹을 시작하세요.
OSS - OopsSec Store
웹 보안 학습을 위해 의도적으로 취약하게 설계된 이커머스 앱.
현실적인 CTF (Capture The Flag) 플랫폼을 통해 실제 공격 벡터를 마스터하세요.
플래그(flags)를 찾고, 취약점을 익스플로잇(exploit)하며, 보안 기술 수준을 높이세요.
Docker Hub · npm · Roadmap · Walkthroughs · Contributing · Good first issues
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

