Mini Shai-Hulud Strikes Again: 314 npm Packages Compromised
TL;DR Highlight
2026년 5월 19일, npm 계정 하나가 탈취되어 22분 만에 637개 악성 버전이 배포됐고, echarts-for-react·size-sensor 등 월 수백만 다운로드 패키지들이 감염되어 AWS 자격증명·SSH 키·AI 코딩 에이전트까지 탈취하는 정교한 공급망 공격이 발생했다.
Who Should Read
npm 패키지를 사용하는 프론트엔드·백엔드 개발자, CI/CD 파이프라인을 운영하는 DevOps 엔지니어, 또는 Claude Code·Codex 등 AI 코딩 에이전트를 업무에 사용하는 개발자.
Core Mechanics
- npm 계정 'atool'이 2026년 5월 19일 탈취되어 단 22분 만에 자동화된 방식으로 317개 패키지에 637개의 악성 버전이 일제히 배포됐다. size-sensor(월 420만 다운로드), echarts-for-react(월 380만), @antv/scale(월 220만), timeago.js(월 115만) 등 인기 패키지들이 포함됐다.
- 악성 페이로드는 498KB짜리 난독화된 Bun 스크립트로, 3주 전 SAP 공급망 공격에 사용된 'Mini Shai-Hulud' 툴킷과 동일한 스캐너 구조·자격증명 정규식·난독화 패턴을 사용한다. 같은 공격자 또는 동일 툴킷을 쓰는 그룹의 소행으로 추정된다.
- 자격증명 수집 범위가 매우 광범위하다. AWS 환경변수·설정파일·EC2 IMDS(인스턴스 메타데이터 서비스)·ECS 컨테이너 메타데이터·Secrets Manager, Kubernetes 서비스 어카운트 토큰, HashiCorp Vault, GitHub PAT, npm 토큰, SSH 키, 그리고 로컬 패스워드 매니저(1Password, Bitwarden, pass, gopass)까지 전부 탈취 대상이다.
- 탈취된 데이터는 두 가지 경로로 외부로 빠져나간다. 첫째는 탈취한 GitHub 토큰으로 공개 저장소에 Git 오브젝트로 커밋하는 방식(User-Agent를 python-requests/2.31.0으로 위장), 둘째는 t.m-kosche[.]com 도메인으로 RSA+AES 암호화된 HTTPS POST를 보내는데 이를 OpenTelemetry 트레이스 데이터로 위장한다.
- CI 환경에서는 GitHub Actions OIDC 토큰을 npm publish 토큰으로 교환하고, Sigstore(Fulcio + Rekor)로 탈취한 신원을 사용해 아티팩트에 서명한 뒤 .github/workflows/codeql.yml에 악성 코드를 주입해 지속성을 확보한다.
- AI 코딩 에이전트를 직접 노린다는 점이 특히 위험하다. Claude Code와 Codex의 SessionStart 훅을 가로채서 AI 세션이 시작될 때마다 악성코드가 재실행되도록 하고, VS Code에는 tasks.json에 'runOn': 'folderOpen' 설정을 주입해 폴더를 열 때마다 실행되게 만든다.
- 지속성을 위해 'kitty-monitor'라는 이름의 systemd 서비스(Linux) 또는 macOS LaunchAgent를 설치한다. 이 백도어는 GitHub의 커밋 검색 API를 1시간마다 폴링해 'firedalazer' 키워드가 포함된 커밋 메시지에서 RSA-PSS로 서명된 명령을 찾아 임의의 Python 코드를 다운로드·실행한다.
- 패키지 설치 방식에 교묘한 이중 감염 경로가 있다. 637개 버전 중 630개는 preinstall 훅(bun run index.js) 외에도 optionalDependencies에 antvis/G2 저장소의 특정 커밋 SHA를 직접 참조하는 항목을 추가했다. 이 커밋들은 브랜치 히스토리에 나타나지 않는 고아(orphan) 커밋으로, 저장소 쓰기 권한 없이도 GitHub의 포크 오브젝트 공유 메커니즘을 이용해 악성 페이로드 두 번째 사본을 호스팅한다.
Evidence
- npm의 라이프사이클 스크립트(preinstall, postinstall 등)를 기본값으로 비활성화해야 한다는 의견이 강하게 제기됐다. 이 기능이 사실상 '기본 내장 임의 코드 실행(built-in ACE)'이고 이번 공격을 포함해 반복되는 npm 웜 공격이 모두 이 경로를 통해 전파된다는 주장이다. 특히 하나의 명령어에 대해 허용했다고 해서 모든 전이적 의존성까지 자동으로 허용되는 현재 방식이 문제라고 지적됐다.
- 이번에 피해를 입은 패키지 목록이 불완전하다는 제보가 있었다. nx-console VS Code 확장(월 220만 다운로드)도 같은 날 이 웜에 감염됐지만 원문 목록에 포함되지 않았다는 댓글이 달렸고, 해당 의존성 체인을 더 추적해야 할 수 있다는 우려가 공유됐다.
- devcontainer나 VM 안에서만 개발 도구를 사용해도 안전하지 않을 수 있다는 경고가 나왔다. 이 악성코드가 Docker 소켓이 존재하면 컨테이너 탈출을 3가지 순차적 방법으로 시도한다는 점이 언급됐고, rootless VM 엔진(Docker 대신 Podman 등)을 사용하라는 조언이 이어졌다.
- Dependabot을 끄고 npm 패키지 버전을 고정(freeze)하는 것이 현실적 대안이 될 수 있다는 의견이 제시됐다. 프론트엔드 패키지의 경우 최근에는 의미 있는 보안 패치보다 공급망 공격 위험이 더 크다는 판단에서, 정적 BOM(Bill of Materials)으로 전환하고 업데이트 주기를 연 1~2회로 줄이자는 제안이다. 한 사용자는 실제로 60일 쿨다운을 적용하고 있다고 밝혔다.
- aube라는 npm/yarn/pnpm 대체 패키지 매니저가 'jailBuilds' 플래그를 통해 네트워크·파일시스템 접근을 제한하는 기능을 제공한다는 정보가 공유됐다. 하지만 댓글에서는 이것도 결국 고양이-쥐 게임(cat/mouse game)에 불과하다는 반응이 함께 달렸다.
How to Apply
- 지금 당장 npm 프로젝트에서 라이프사이클 스크립트를 비활성화하려면, 프로젝트 루트의 .npmrc 파일에 'ignore-scripts=true'를 추가하거나 설치 시 'npm install --ignore-scripts' 플래그를 사용하라. 대부분의 패키지는 이 스크립트 없이도 정상 작동하며, 꼭 필요한 경우에만 개별적으로 허용하는 방식이 훨씬 안전하다.
- semver 범위(^, ~)를 사용하는 패키지 중 echarts-for-react, size-sensor, timeago.js, @antv/* 계열이 의존성에 있다면 즉시 'npm audit'으로 확인하고, package-lock.json을 검토해 2026년 5월 19일에 해당 패키지의 새 버전이 설치됐는지 확인해야 한다. 감염 여부가 의심된다면 AWS·GitHub·npm 토큰을 즉시 로테이션하라.
- CI/CD 파이프라인에서 GitHub Actions를 사용하고 있다면, OIDC 토큰 발급 권한 범위를 최소화하고 .github/workflows/ 디렉토리의 워크플로우 파일 변경을 모니터링하는 규칙을 추가하라. 이번 악성코드가 codeql.yml을 직접 수정해 지속성을 확보했으므로, 워크플로우 파일 변경에 대한 PR 리뷰나 코드 소유자 승인을 의무화하는 것이 효과적이다.
- Claude Code나 Codex 등 AI 코딩 에이전트를 로컬에서 사용 중이라면, 에이전트가 실행되는 환경에 감염된 npm 패키지가 설치되어 있을 경우 SessionStart 훅이 탈취돼 매 세션마다 악성코드가 실행될 수 있다. 에이전트를 실행하는 머신과 패키지를 설치하는 환경을 분리(별도 컨테이너 또는 VM)하고, rootless 모드로 실행하는 것을 검토하라.
Code Example
# .npmrc에 추가하여 라이프사이클 스크립트 전역 비활성화
ignore-scripts=true
# 또는 설치 시 명시적으로 비활성화
npm install --ignore-scripts
# pnpm의 경우
pnpm install --ignore-scripts
# 감염 여부 빠른 확인 (2026-05-19 이후 변경된 패키지 확인)
npm audit
git log --since='2026-05-19' -- package-lock.json
# aube 패키지 매니저의 jailBuilds 플래그 사용 예시 (네트워크/파일시스템 접근 제한)
aube install --jailBuildsTerminology
Related Papers
Show HN: Forge – Guardrails take an 8B model from 53% to 99% on agentic tasks
작은 로컬 LLM(8B)에 guardrails(구조적 안전망)를 씌워 멀티스텝 에이전트 작업 성공률을 53%에서 99%까지 올린 Python 프레임워크 Forge 공개. 모델 자체는 건드리지 않고 실행 환경을 강화하는 접근법이라 주목받고 있음.
Show HN: Semble – Code search for agents that uses 98% fewer tokens than grep
AI 에이전트가 코드베이스를 탐색할 때 grep+파일 읽기 대신 자연어로 관련 코드 스니펫만 뽑아주는 검색 라이브러리로, 토큰 사용량을 약 98% 줄여준다.
Zerostack – A Unix-inspired coding agent written in pure Rust
Claude Code나 OpenCode처럼 메모리를 수 GB씩 잡아먹는 코딩 에이전트 대신, Rust로 만든 초경량(~8MB RAM) 코딩 에이전트 Zerostack이 공개됐다. 저사양 환경에서도 쓸 수 있고, 직접 만든 유사 프로젝트들과 비교 토론이 활발하게 이뤄지고 있다.
Δ-Mem: Efficient Online Memory for Large Language Models
LLM의 컨텍스트 윈도우를 늘리지 않고도 과거 정보를 효율적으로 기억할 수 있는 경량 메모리 모듈 δ-mem을 제안한 논문. 모델 자체를 바꾸거나 파인튜닝 없이 기존 LLM에 붙여서 장기 기억 성능을 높일 수 있어 에이전트 시스템 개발자에게 관심을 끌고 있다.
How Claude Code works in large codebases
Anthropic이 수백만 줄짜리 모노레포, 레거시 시스템, 수십 개 마이크로서비스 환경에서 Claude Code를 운영한 패턴을 정리한 글이다. RAG 방식 대신 에이전틱 검색을 쓰는 이유와 실제 현장의 한계를 함께 확인할 수 있다.
OpenDeepThink: Parallel Reasoning via Bradley--Terry Aggregation
LLM 여러 답안을 토너먼트 방식으로 비교·진화시켜 외부 검증기 없이도 경쟁 프로그래밍 Elo를 +405 올린 프레임워크