단 하나의 라이프사이클 스크립트 없이 침해된 57개의 npm 패키지
요약
공격자가 npm 라이프사이클 스크립트 모니터링을 우회하기 위해 `binding.gyp` 파일을 이용해 57개의 패키지를 침해했습니다. 이 방식은 별도의 스크립트 없이도 네이티브 애드온 빌드 과정을 통해 악성 코드를 실행하며, 자격 증명 탈취 및 AI 코딩 도구 설정 파일 변조를 목적으로 합니다.
핵심 포인트
- binding.gyp를 이용해 postinstall 등 보안 도구의 감시를 우회함
- npm 토큰, AWS, GitHub PAT 등 주요 자격 증명을 탈취함
- Claude, Cursor 등 AI 코딩 어시스턴트 설정 파일까지 공격 범위에 포함
- 라이프사이클 스크립트 외의 빌드 프로세스에 대한 보안 점검 필요
모든 주요 npm 보안 도구는 라이프사이클 스크립트 (lifecycle scripts)를 모니터링합니다. Socket, Snyk, npm audit — 이들은 모두 preinstall 및 postinstall 훅 (hooks)을 감시합니다. 6월 3일, 한 공격자는 이 중 어떤 것도 트리거하지 않고 57개 패키지에 걸쳐 286개의 악성 버전을 배포했습니다.
기술적 수법: 157바이트 크기의 binding.gyp 파일.
binding.gyp가 모니터링을 우회하는 방법
npm은 설치 중에 binding.gyp를 발견하면, 네이티브 애드온 (native addons)을 컴파일하기 위해 자동으로 node-gyp rebuild를 실행합니다. 별도의 라이프사이클 스크립트가 필요하지 않습니다. 공격자의 binding.gyp는 아무것도 컴파일하지 않았습니다. 대신 빌드 액션 (build action)으로서 node -e "<payload>"를 통해 임의의 JavaScript를 실행했습니다.
postinstall과 동일한 결과입니다. 스크립트 모니터링 도구로부터의 경고는 전혀 없었습니다.
가장 큰 피해 사례: 월간 다운로드 수가 408,000회에 달하는 @vapi-ai/server-sdk입니다. 나머지는 autotel (27개 패키지), awaitly (6개), executable-stories (9개), node-env-resolver (5개) 등 4개의 패키지 제품군에 걸쳐 있었습니다.
타임라인: 3주 동안 발생한 3가지 벡터
이것은 단독 공격이 아니었습니다. Phantom Gyp는 동일한 멀웨어 (malware) 제품군(Miasma/Shai-Hulud)에서 발생한 세 번째 파도입니다:
| 날짜 | 벡터 (Vector) | 증명된 내용 |
|---|---|---|
| 5월 11일 | postinstall 스크립트 | 자가 전파형 웜 (worm), 39분 만에 637개 패키지 침해 |
| ... |
각 파도는 동일한 방어 계층에 대해 서로 다른 실행 벡터를 테스트했습니다. postinstall이 탐지되자, 그들은 위조된 출처 (provenance)를 사용하여 preinstall로 이동했습니다. 그것마저 탐지되자, 그들은 라이프사이클 스크립트를 완전히 제거했습니다.
페이로드 (Payload)
표준적인 Miasma 자격 증명 탈취 방식입니다: npm 토큰, GitHub PATs, AWS 자격 증명, GCP 서비스 계정, Azure 키, HashiCorp Vault 토큰, Kubernetes 설정 등입니다. 탈취된 정보는 공격자가 제어하는 GitHub 리포지토리 (repos)를 통해 유출됩니다. 주입된 GitHub Actions 워크플로 (workflows)를 통해 지속성을 유지하며, 침해된 관리자 계정으로 재배포함으로써 자가 전파를 수행합니다.
이 공격은 또한 AI 코딩 어시스턴트(AI coding assistants)를 겨냥한 설정 파일인 .claude/setup.mjs, .cursor/rules/setup.mdc, .gemini/settings.json을 심어두었습니다. 귀하의 보안 도구는 라이프사이클 스크립트 (lifecycle scripts)를 모니터링합니다. 하지만 네이티브 애드온 (native addon) "빌드 (build)" 과정 중에 귀하의 IDE 설정 디렉토리에 무엇이 작성되는지는 아무도 모니터링하지 않습니다.
행동 점수 (behavioral scoring)가 보여주는 것
저는 영향을 받은 패키지들을 Commit의 감사 (audit)를 통해 실행해 보았습니다:
| 패키지 | 점수 | 게시자 (Publishers) | 주간 다운로드 |
|---|---|---|---|
| @vapi-ai/server-sdk | 57 | 5 | 60k |
| ... |
60점을 넘는 패키지는 단 하나도 없었습니다. 가장 높은 점수인 @vapi-ai/server-sdk(57점)는 5명의 게시자가 있지만, 이조차도 '안전 (OK)' 기준인 75점보다 훨씬 낮습니다. 나머지는 채택률이 매우 낮은 단일 게시자 패키지들입니다.
고위험 (HIGH-risk) 패키지(점수 < 40)를 차단하도록 설정된 게이트 (gate)는 첫 설치 시 대부분을 잡아냅니다. 임계값(threshold)을 경고 (WARNING, < 60)로 높이면, 라이프사이클 스크립트, CVE, 또는 출처 서명 (provenance signatures)을 확인하지 않고도 57개 패키지 모두를 잡아낼 수 있습니다.
binding.gyp 트릭은 새로운 방식입니다. 하지만 '이력이 없는 단일 게시자' 패턴은 새로운 것이 아닙니다. 행동 점수 (behavioral scoring)는 모든 기술을 가능하게 만드는 구조적 패턴을 포착합니다.
조치 사항
AI 어시스턴트의 패키지 설치를 게이트 (gate) 하세요:
npx proof-of-commitment hook
Cursor와 Claude Code에서 발생하는 모든 npm install, pip install, cargo add, go get을 가로챕니다. 행동 임계값 미만의 패키지는 귀하의 기기에 도달하기 전에 차단됩니다.
현재 설치된 항목을 감사 (audit) 하세요:
npx proof-of-commitment --file package-lock.json
CI에 추가하세요:
- run: npx -y proof-of-commitment --fail-on=critical
Phantom Gyp는 3월 이후 발생한 여섯 번째 주요 공급망 공격 (supply chain attack)입니다. LiteLLM, axios, Shai-Hulud, TrapDoor, Miasma/Red Hat, 그리고 이제 이것입니다. 각각의 공격은 새로운 실행 벡터 (execution vector)를 찾아냅니다. 방어자들은 기술별 모니터를 계속 추가하고 있으며, 공격자들은 기술을 계속 바꾸고 있습니다.
행동 점수 산정 (Behavioral scoring)은 기술을 쫓지 않습니다. 대신 그 모든 기술을 가능하게 만드는 구조적 위험을 측정합니다. 즉, '이 패키지는 전체 배포 권한을 가진 단 한 명의 관리자에 의해 유지되고 있으며, 커뮤니티가 없고, 이력이 없는가?'를 묻는 것입니다. 이 질문은 다음 벡터 (vector) 또한 포착해냅니다.
Commit은 npm, PyPI, Cargo, 그리고 Go 패키지를 행동적 헌신 (behavioral commitment)을 기준으로 점수화합니다. 이는 별점 (stars), README, 또는 다운로드 수보다 조작하기 어려운 신호입니다. 감사 시도하기 또는 귀하의 AI 어시스턴트에 MCP 서버를 추가하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기