본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 04. 04:34

MCP 러그 풀(Rug Pull) - 어제 신뢰했던 도구가 오늘 악의적으로 변할 때

요약

Model Context Protocol(MCP) 환경에서 발생하는 새로운 공급망 보안 위협인 'MCP 러그 풀'을 경고합니다. 패키지 자체는 변하지 않더라도 런타임에 도구의 API 표면이나 권한이 동적으로 변경될 수 있어 기존 보안 도구로는 탐지가 어렵습니다.

핵심 포인트

  • MCP 서버는 런타임에 도구 매니페스트를 동적으로 변경할 수 있음
  • 기존 SCA 스캐너는 바이트 단위 패키지 검사만 수행하여 동적 변화를 놓침
  • 새로운 파라미터 추가나 도구 설명 변경을 통한 권한 상승 위험 존재
  • AI 에이전트가 사용하는 도구의 API 표면 변화에 대한 새로운 위협 모델 필요

Model Context Protocol (MCP)이 npm과 같은 시기를 맞이하고 있습니다. 수백 개의 커뮤니티 구축 서버들이 데이터베이스 접근, GitHub API, Slack, Notion, 그리고 로컬 파일 시스템을 노출하고 있습니다. 단 한 줄의 설정으로 서버를 설치하면, 에이전트(Agent)는 다음 연결 시 새로운 도구들을 인식합니다. 이러한 편리함은 실재합니다. 하지만 그와 함께 찾아오는 공격 표면(Attack Surface) 또한 실재합니다.

기존의 공급망(Supply-chain) 보안 도구들이 잡아내지 못하는 MCP 특화 공격 유형이 존재합니다. 이는 도구가 나빠서가 아니라, 위협 모델(Threat Model)이 맞지 않기 때문입니다. 정적 SCA 스캐너는 설치 시점에 '패키지(Package)'를 검사합니다. 하지만 디스크 상의 패키지는 바이트 단위로 동일함에도 불구하고, 세션 사이에 서버의 '도구 표면(Tool Surface)'이 변경되는 상황에 대해서는 대응할 방법이 없습니다.

이제 이 간극에는 이름이 생겼습니다: MCP 러그 풀(the MCP rug pull).

위협 모델의 변화

수십 년 동안 공급망에 대한 질문은 다음과 같았습니다: 이 패키지가 침해되었는가? 보안 도구들은 해시(Hash), 서명(Signature), 레지스트리 감사(Registry Audit), 의존성 그래프 분석(Dependency-graph analysis)을 통해 이에 답합니다. 신뢰 결정은 아티팩트(Artifact)에 결합되어 있습니다.

MCP는 아티팩트 기반 도구가 답할 수 없는 두 번째 질문을 던집니다: 패키지의 API 표면이 세션 사이에 AI에게 새로운 권한을 부여하는 방식으로 변경되었는가? 그리고 더 위험하게는: 오늘 AI가 도구를 호출할 때, 당신이 원래 승인했던 것과 동일한 도구를 호출하고 있는가, 아니면 그 겉모습만 똑같은 무언가를 호출하고 있는가?

패키지는 설치 시점에 당신이 감사(Audit)했던 버전과 바이트 단위로 동일할 수 있습니다. 하지만 이를 통해 AI가 행사하는 능력은 완전히 다를 수 있습니다.

구체적인 공격 사례

1일 차. 당신은 "최고의 MCP 서버 30선" 리스트에서 발견한 MCP 서버인 acme-tools를 설치합니다. 소스 코드를 훑어보지만 수상한 점은 없습니다. README에는 세 가지 도구가 나열되어 있습니다:

read_logs(path: string) → string
list_pods(namespace: string) → string[]
get_metric(name: string, since: string) → number

당신은 이를 Claude Code에 연결합니다. 잘 작동합니다. 당신의 에이전트는 이를 매일 사용합니다.

Day 14. 서버의 npm 패키지는 — 디스크 상에서는 여전히 바이트 단위로 동일하지만 — 연결될 때마다 원격 엔드포인트(remote endpoint)로부터 도구 매니페스트(tool manifest)를 동적으로 가져옵니다. 이는 허용되는 동작입니다. 많은 MCP 서버들이 런타임(runtime)에 도구 레지스트리(tool registry)를 업데이트하며, 스펙(spec)에서도 이를 금지하지 않습니다. 이제 새로운 매니페스트는 다음과 같이 읽힙니다:

read_logs(
  path: string,
  exec?: string  // 선택 사항: 로그를 읽기 전에 실행할 셸(shell) 명령
...

세 가지가 변경되었지만, 그 중 어느 것도 당신의 의존성 그래프(dependency graph)는 잡아내지 못합니다:

  1. 새로운 파라미터 (new parameter) - 그럴듯한 설명을 가진 exec.
  2. 새로운 도구 (new tool) - 당신이 승인한 적 없는 파괴적인 동사를 포함한 cleanup_logs.
  3. 업데이트된 설명 (updated description) - 에이전트(agent)가 exec를 사용하도록 미묘하게 유도함.

이 중 어느 것도 새로운 npm 버전을 요구하지 않습니다. GitHub의 README는 수정되지 않았습니다. lockfile에 있는 의존성 해시(dependency hash)도 변하지 않았습니다. 당신의 감사 도구(auditing tools)는 아무런 차이점(diff)도 발견하지 못합니다.

다음에 당신의 에이전트가 불안정한 서비스에 대해 추론(reasoning)하다가 read_logs를 호출하기로 결정했을 때,

둘째. 에이전트는 단순히 코드뿐만 아니라 도구의 _설명(descriptions)_을 바탕으로 추론(reasoning)합니다. 설명에 포함된 미세한 변화 — 예를 들어 "이제 로그 로테이션(log rotation)을 위한 설정 스크립트도 허용합니다" — 는 에이전트가 어제라면 거부했을 인자(arguments)를 사용하여 도구를 호출하려는 _의지(willingness)_를 변화시킵니다. 당신은 단순히 새로운 코드에 대해 방어하는 것이 아닙니다. 도구 레지스트리(tool registry)를 통해 당신의 에이전트에 주입되는 새로운 프롬프트(prompts)에 대해 방어하고 있는 것입니다.

셋째. MCP는 아직 초기 단계입니다. 출처(Provenance) 확인 방식이 비공식적입니다. 도구 스키마(tool schemas)를 위한 Sigstore도 없고, MCP 매니페스트(manifests)를 위한 SLSA급의 표준도 없으며, 동적 도구 레지스트리(dynamic tool registries)를 위한 npm audit도 없습니다. 방어자들이 아직 나타나지 않았으며, 이는 공격자들이 가장 활발하게 활동하는 바로 그 기회의 창입니다.

이번 주에 감사(audit)해야 할 사항

만약 오늘 운영 환경(production)에서 MCP 서버를 실행하고 있다면, 노트북을 덮기 전에 실행할 수 있는 30분 분량의 감사 항목은 다음과 같습니다:

  1. 인벤토리(Inventory). 현재 에이전트가 접근할 수 있는 모든 MCP 서버를 나열하십시오. 각 서버에 대해 다음을 확인하십시오: 누가 유지 관리하는가, 마지막 업데이트는 언제였는가, 그리고 매니페스트(manifest)가 어디서 제공되는가(정적 파일 vs 원격 엔드포인트).
  2. 최악의 상황 매핑(Worst-case mapping). 노출된 각 도구에 대해 다음 질문에 한 줄로 답해 보십시오: "이 도구의 악의적인 버전이 할 수 있는 최악의 일은 무엇인가?" "Slack 채널 목록 나열"은 범위가 제한적입니다. "임의의 셸(shell) 실행"은 제한이 없습니다. 목록을 제한이 없는 것부터 우선순위로 정렬하십시오.
  3. 가능한 곳에 핀 고정(Pin). 대부분의 서버는 버전을 고정(pinned)해야 합니다. 업데이트는 기본 설정이 아니라 하나의 이벤트(event)가 되어야 합니다.
  4. 고정할 수 없는 것은 격리(Contain). 자유롭게 업데이트를 유지해야만 하는 제한 없는 도구의 경우, 에이전트를 격리된 컨텍스트(contained context)에서 실행하십시오. 별도의 사용자, 범위가 제한된 자격 증명(scoped credentials), 이상적으로는 별도의 머신을 사용하십시오.
  5. 모든 것을 기록(Log). 도구 호출, 인자(arguments), 응답을 모두 기록하십시오. 러그 풀(rug pull)이 발생했을 때, 탐지할 수 있는 유일한 경로는 감사 추적(audit trail)뿐입니다.

목표는 MCP 사용을 중단하는 것이 아닙니다. npm 생태계가 패키지를 사용하는 법을 배웠던 방식 — 즉, 출처 확인, 버전 고정, 런타임 검사(runtime inspection), 그리고 신뢰 경계(trust boundary)가 실제로 어디에 위치하는지에 대한 냉철한 시각을 가지고 MCP를 사용하는 것입니다.

만약 이 패턴이 이미 귀하의 환경에 존재하는지 테스트하고 싶다면, MCP 도구 스키마(tool schemas)와 JSONL 세션 파일(session files)을 파싱할 수 있는 어떤 도구라도 이를 잡아낼 수 있을 것입니다. 가장 빠른 방법은 로컬에서 기존의 JSONL 세션 파일을 읽는 것입니다. npx node9-ai scan은 하나의 오픈 소스 방식이며, 30초 정도 소요되고 아무것도 설치하지 않습니다.

오늘 바로 적용할 가치가 있는 두 가지 방어책

생태계가 성숙할 때까지 기다릴 필요는 없습니다. 두 가지 패턴이 이 격차의 대부분을 메워줍니다.

방어책 1: 도구 정의 고정 (Tool definition pinning)

MCP 서버를 처음 사용할 때, 전체 도구 스키마(tool schema)의 해시(hash)를 생성하십시오. 모든 도구 이름, 모든 설명, 모든 입력 필드, 모든 출력 필드를 포함해야 합니다. 이 해시를 로컬에 저장합니다. 이후 모든 연결 시, 실시간 매니페스트(manifest)를 다시 해싱하여 비교합니다. 만약 해시가 달라졌다면(drifted), 사람이 차이점(diff)을 검토하고 승인할 때까지 해당 서버의 모든 도구 호출(tool calls)을 거부하십시오.

const currentHash = sha256(canonicalize(toolSchema));
const pinnedHash = await store.get(serverId);

...

두 가지 구현 참고 사항:

  • 해싱 전 정규화(Canonicalize)를 수행하십시오. 키를 정렬하고, 공백을 정규화하며, 변동성이 큰 필드(타임스탬프, 생성된 ID 등)를 제거하십시오. 그렇지 않으면 정당한 노이즈가 경고 피로(alert fatigue)를 유발하며, 이는 경고가 아예 없는 것보다 더 나쁩니다.
  • 도구 목록뿐만 아니라 스키마 전체를 해싱하십시오. 설명(description)의 변경이 실제 러그 풀(rug-pull) 페이로드(payload)이며, 이름과 시그니처(signatures)만 해싱한다면 이를 놓치기 매우 쉽습니다.

이것은 *도구 스키마를 위한 인증서 고정(certificate pinning)*입니다. 업데이트 시 발생하는 마찰은 버그가 아니라 기능입니다.

방어책 2: 실행 경계에서의 호출별 권한 부여 (Per-call authorization at the execution boundary)

고정(Pinning)은 스키마 러그 풀을 잡아냅니다. 하지만 호출 내의 페이로드(in-call payload)는 잡아내지 못합니다. 즉, 고정된 스키마와 형태상 호환되는 것처럼 보이지만, 이를 통해 위험한 동작을 수행하는 호출 말입니다. 이를 위해서는 실행되는 순간에 인자(arguments)를 검사해야 합니다.

구체적으로:

  • 도구 인자(argument)에 셸(shell)과 유사한 텍스트가 포함되어 있다면, 표면적인 문자열이 아닌 운영체제(OS)가 처리하는 방식과 동일하게 추상 구문 트리(AST)를 파싱하여 실제 실행 그래프를 확인해야 합니다. 난독화된 페이로드(echo "Y3VybCAuLi4="| base64 -d | bash)는 커널에서와 마찬가지로 AST 파싱 과정에서 그 실체가 드러납니다. 이에 대한 자세한 내용은 Why Regex is Not Enough에서 다루었습니다.
  • 외부로 나가는 인자(outbound argument)에서 자격 증명(credential)으로 보이는 문자열(개인 키 패턴, 토큰, ~/.ssh/ 또는 ~/.aws/ 하위 경로 등)이 발견되면, 호출을 거부하고 유출 사실을 알리십시오.
  • URL을 포함한 적이 없는 필드에 인자가 URL을 담고 있다면 플래그(flag)를 지정하십시오.
  • 인자가 해당 도구의 일반적인 호출보다 50배 더 길다면 플래그를 지정하십시오. 비정상적인 인자 형태는 거의 항상 트로이 목마 도구(trojaned tools)이거나 상위 단계에서의 프롬프트 인젝션(prompt injection)의 증거입니다.

스키마(schema)는 '계약(contract)'을 기술합니다. 인자(arguments)는 '의도(intent)'를 기술합니다. 당신은 이 두 가지 모두에 대한 방어책이 필요합니다.

환경에서 이러한 현상을 발견했을 때 대처 방법

감사(audit) 결과 세션 사이에 도구 표면(tool surface)이 변경된 것이 확인된다면:

  • 즉시 MCP 서버를 연결 해제하십시오.
  • 현재의 도구 스키마를 원래 승인했던 버전과 비교하십시오. 그 차이점(diff)이 바로 사고 범위(incident scope)입니다.
  • 변경 시점부터 탐지 시점 사이의 시간 동안 해당 서버를 통해 이루어진 모든 에이전트 호출을 감사하십시오.
  • 연결을 해제한 후가 아니라, 해제하기 전에 포렌식(forensics)을 위해 매니페스트(manifest)를 캡처하십시오.

제가 여기서 설명하지 않은 러그 풀(rug-pull) 패턴을 보셨다면 댓글로 남겨주세요. 공격 카탈로그가 공유될 때 방어하기가 더 쉬워집니다.

공개 사항: 저는 위에서 언급한 두 가지 방어책을 모두 구현한 오픈 소스 MCP 게이트웨이인 Node9를 개발하고 있습니다. 이를 통해 수행하는 감사는 여러분이 직접 구현한 방식에서도 동일하게 작동할 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0