Mini Shai-Hulud: 대규모 npm 공급망 공격의 해부
npm 생태계가 다시 한 번 대규모 공급망 침해의 피해자가 되었습니다. 2026년 5월 19일, npm 계정 atool이 침해되어 317개의 서로 다른 패키지에 걸쳐 637개의 악성 버전이 22분 동안 자동으로 빠르게 배포되었습니다.
이는 단순한 자격 증명 탈취가 아니었습니다. 공격자는 "Mini Shai-Hulud"라는 정교한 툴킷을 배포했으며, 이는 몇 주 전 고프로파일 SAP 침해에 사용된 프레임워크의 직접적인 진화형입니다. size-sensor와 echarts-for-react와 같은 영향을 받은 패키지는 월간 수백만 건의 다운로드를 기록하고 있어, 공격 범위는 방대했으며 AWS 인프라부터 신흥 AI 코딩 에이전트까지 모두를 대상으로 했습니다.
감염 벡터: Preinstall 훅을 넘어
공격자는 페이로드가 대상 시스템에 도달하도록 두 가지 주요 실행 경로를 활용했습니다:
preinstall훅: 모든 침해된 버전은preinstall스크립트(bun run index.js)를 추가했습니다. npm이 semver 범위(예:^3.0.6)를 가장 최신 버전으로 해석하기 때문에, 클린 설치를 수행하는 프로젝트는latest태그와 관계없이 자동으로 악성 업데이트를 가져왔습니다.- GitHub 사칭 커밋: 더 교묘한 수법으로, 630개의 버전이
optionalDependencies항목을 삽입해antvis/G2저장소를 가리키게 했습니다. 공격자는 "orphan commits"(포크에 푸시된 커밋으로, 부모 저장소 네임스페이스를 통해 SHA로 가져올 수 있음)를 사용했습니다. 저자를 정당한 유지관리자로 위장함으로써, 공격자는 대상 저장소에 대한 쓰기 권한 없이도 GitHub에 페이로드의 두 번째 복사본을 호스팅했습니다.
기술 심층 분석: Mini Shai-Hulud 툴킷
자격 증명 수집 및 클라우드 탐색
페이로드는 최대 추출을 위해 설계된 498KB 난독화된 Bun 스크립트입니다. 이 스크립트는 80개 이상의 환경 변수를 대상으로 하는 스캐너 아키텍처를 사용하며, 정규식 패턴을 이용해 다음을 식별합니다:
- 클라우드 키: AWS(EC2 IMDSv2 및 ECS 메타데이터 포함), GCP 서비스 계정, Azure 자격 증명.
- 토큰: GitHub PAT, npm 토큰, Slack 토큰, HashiCorp Vault 토큰.
- 인프라: Kubernetes 서비스 계정 토큰 및 SSH 키.
컨테이너 탈출 및 권한 상승
악성코드가 Docker 소켓(예: /var/run/docker.sock)을 감지하면 컨테이너 탈출을 시도합니다. 호스트 바인드 마운트를 포함한 특권 Docker 컨테이너를 구성하여 공격자에게 호스트 파일 시스템에 대한 전체 접근 권한을 부여합니다.
AI 에이전트 탈취
이 공격의 가장 새로운 측면 중 하나는 AI 코딩 도구를 표적으로 삼는 것입니다. 페이로드는 .claude/settings.json(Claude Code용)과 .vscode/tasks.json(Codex/VS Code용)에 SessionStart 훅을 삽입합니다.
개발자가 AI 세션을 시작하거나 프로젝트 폴더를 열 때마다 Bun 부트스트래퍼가 트리거되어 악성코드를 다시 실행합니다. 이는 초기 npm 감염이 정리되더라도 개발자의 AI 지원 워크플로우를 통해 악성코드가 지속됨을 보장합니다.
C2 인프라: GitHub를 은밀한 드롭으로 활용
의심스러운 외부 서버에 연결하는 대신, 공격자는 GitHub API를 명령 및 제어(C2) 채널로 사용하여 트래픽이 일반 개발자 활동과 섞이도록 만들었습니다.
- 유출: 도난당한 데이터는 침해된 토큰으로 생성된 공개 저장소에 Git 객체로 커밋되었습니다. 이러한 저장소는 Dune 테마의 이름 패턴(예:
fremen-sandworm-315)을 따르며 설명은 "Shai-Hulud: Here We Go Again." 입니다. kitty-monitor백도어: 페이로드는kitty-monitor라는 지속적인 systemd 서비스 또는 macOS LaunchAgent를 설치합니다. 이 Python 데몬은firedalazer키워드를 포함한 커밋을 찾기 위해 GitHub Search API를 매시간 폴링합니다. 유효한 RSA-PSS 서명이 있는 커밋을 발견하면 서명된 URL에서 임의의 Python 코드를 다운로드하여 실행합니다.
CI/CD 파괴 및 Sigstore 악용
CI 환경에서는 공격이 더욱 위험해집니다. 페이로드는 GitHub Actions OIDC 토큰을 npm publish 토큰으로 교환하여 공격자가 파이프라인 자체의 신원을 사용해 새로운 패키지를 게시할 수 있게 합니다.
또한 공격자는 **Sigstore(Fulcio + Rekor)**를 악용해 아티팩트를 서명했습니다. 도난당한 OIDC 토큰을 사용해 위조된 출처와 함께 정식 서명된 아티팩트를 생성함으로써, Sigstore를 신뢰 검증에 의존하는 하위 보안 스캐너를 속일 가능성이 있습니다.
커뮤니티 반응 및 방어 전략
이 공격의 규모는 npm 신뢰 모델의 본질적인 불안정성에 대해 개발자 커뮤니티 내에서 큰 논쟁을 일으켰습니다.
"고양이와 쥐" 게임
많은 개발자들이 lifecycle 스크립트(preinstall/postinstall)에 의존하는 것에 불만을 표했습니다. 한 댓글자는 다음과 같이 언급했습니다:
"Lifecycle 스크립트는 NPM에서 기본적으로 비활성화되어야 합니다. 이는 내장된 임의 코드 실행 기능을 제공하는 편의 기능이며... 이러한 광범위한 NPM 웜 스타일 공격이 모두 이를 통해 전파되었습니다."
권장 완화 방안
유사한 공급망 공격으로부터 보호하기 위해 보안 연구원 및 커뮤니티 구성원은 다음을 제안합니다:
- 의존성 쿨다운:
npm config set min-release-age=2와 같은 설정을 사용해 최근 며칠 내에 게시된 패키지 설치를 피합니다. 대부분의 악성 급증이 이 시기에 감지되고 제거됩니다. - 강화된 환경: 루트리스 VM이나 DevContainer(예: Docker 대신 Podman 사용)에서 개발 워크로드를 실행하여 컨테이너 탈출 시도를 완화합니다.
- Lockfile 감사:
pmg(Package Manager Guard) 또는vet와 같은 도구를 사용해 lockfile에서 예상치 못한 크기 급증이나 새로운 lifecycle 스크립트를 분석합니다. - 벤더링 의존성: 중요한 프로젝트의 경우, 의존성을 벤더링(자신의 저장소에 복제하고 고정)하여 상위 침해 위험을 없앨 것을 고려합니다.
침해 지표 요약 (IoC)
| 카테고리 | 지표 |
|---|---|
| 페이로드 SHA256 | a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c |
| C2 키워드 | firedalazer in GitHub commit messages |
| 지속성 | kitty-monitor.service or com.user.kitty-monitor.plist |
| 파일 | ~/.local/share/kitty/cat.py, .claude/setup.mjs, .vscode/setup.mjs |
| 저장소 패턴 | {DuneWord1}-{DuneWord2}-{0-999} (e.g., mentat-melange-123) |
SUMMARY: 침해된 npm 계정으로 인해 317개의 패키지가 감염되었으며, 클라우드 자격 증명을 수집하고 AI 에이전트를 탈취하며 지속적인 GitHub 기반 C2 백도어를 구축하는 정교한 툴킷이 배포되었습니다.
TITLE: Mini Shai-Hulud: 대규모 npm 공급망 공격의 해부