
TanStack/Mini Shai-Hulud npm 공급망 공격(CVE-2026-45321)에 대한 IOC 검사기
curl -fsSL https://raw.githubusercontent.com/nkopylov/tanscript-exploit-check/main/check-tanstack-exploit.sh | bash
또는 클론하여 로컬에서 실행:
git clone https://github.com/nkopylov/tanscript-exploit-check.git
cd tanscript-exploit-check
./check-tanstack-exploit.sh [프로젝트_디렉토리 ...]
이 점검기는 TeamPCP의 미니 샤이-훌루드 공급망 공격 캠페인 두 차례의 물결을 다룹니다:
2026년 5월 11일 (19:20-19:26 UTC), 공격자가 6분 간격으로 42개의 @tanstack/* npm 패키지에 걸쳐 84개의 악성 버전을 게시했습니다. 이 공격은 Mistral AI, UiPath, OpenSearch 등의 패키지에도 영향을 미쳐 npm과 PyPI 전체에서 총 170개 이상의 패키지가 피해를 입었습니다.
유지 관리자 자격 증명은 도난당하지 않았습니다. 공격자는 3단계 공격을 통해 CI/CD 신뢰 체인 자체를 악용했습니다:
pull_request_target 익스플로잇 — 버려진 GitHub 포크가 기본 리포지토리의 보안 컨텍스트에서 공격자 코드를 실행하는 PR을 열었습니다2026년 5월 19일 (01:39-02:06 UTC), 손상된 atool npm 계정 ([email protected])이 두 차례의 자동화된 물결로 314개 패키지에 걸쳐 637개의 악성 버전을 게시했습니다. 영향이 큰 대상은 다음과 같습니다:
size-sensor (월 420만 다운로드)echarts-for-react (월 380만 다운로드)@antv/scale (월 220만 다운로드)timeago.js (월 115만 다운로드)@antv/* 및 기타 패키지이 물결은 Bun 기반 페이로드(498KB index.js)를 "preinstall": "bun run index.js"를 통해 트리거했으며, 2단계 페이로드는 포크 객체 공유 익스플로잇을 통해 antvis/G2 GitHub 리포지토리에 푸시된 가짜 커밋에 숨겨졌습니다.
두 물결 모두 동일한 "미니 샤이-훌루드" 도구 키트 계열을 사용합니다:
gh-token-monitor(1차 물결) 및 kitty-monitor(2차 물결) 데몬(LaunchAgent/systemd 이용); .claude/settings.json 및 .vscode/tasks.json에 훅 삽입toJSON(secrets)를 덤프하는 워크플로우 인젝션, npm OIDC 토큰 교환rm -rf ~/ 실행firedalazer 키워드를 폴링, RSA-PSS 서명된 명령 수신@tanstack/router* 및 @tanstack/start* 패키지만 영향을 받았습니다. 영향 없음: @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, @tanstack/store.
전체 42개 패키지 목록은 전체 권고를 참조하세요.
npm 사용자 atool([email protected])이 게시한 모든 패키지가 2026년 5월 19일에 악성 버전을 받았습니다. 영향이 큰 패키지는 다음과 같습니다:
| 패키지 | 월간 다운로드 수 |
|---|---|
size-sensor | 420만 |
echarts-for-react | 380만 |
@antv/scale | 220만 |
timeago.js |
그 외 310개 이상의 패키지는 주로 @antv/* 범위(@antv/g2, @antv/g6, @antv/l7, @antv/s2, @antv/x6, @antv/f2 등), ai-figure, timeago-react, jest-canvas-mock, jest-date-mock 등입니다.
전체 목록은 SafeDep 분석을 참조하세요.
중요: 토큰을 취소하기 전에 데드맨 스위치를 비활성화하십시오. 데몬이 활성 상태인 동안 토큰이 취소되면 악성코드가
$HOME을 삭제합니다.
gh-token-monitor 및 kitty-monitor)을 종료하고 LaunchAgent/systemd 서비스를 제거하십시오.claude/router_runtime.js, .vscode/setup.mjs, ~/.local/share/kitty/cat.py, /var/tmp/.gh_update_state 등)을 삭제하십시오node_modules와 잠금 파일을 삭제하고 --ignore-scripts로 다시 설치하십시오api.masscan.cloud, filev2.getsession.org, git-tanstack.com, t.m-kosche.com)다음 프롬프트를 AI 코딩 에이전트(Claude Code, Cursor, Cline/OpenClaw, Windsurf, Hermes 등)에 복사하여 붙여넣으면 이와 같은 공급망 공격에 대해 프로젝트를 강화할 수 있습니다.
공급망 공격에 대비하여 이 프로젝트의 npm 구성을 강화하십시오. 다음을 수행하십시오:
정확한 버전 고정:
package.json의 모든 종속성에서^및~접두사를 제거하여npm install이 새로 게시된 버전을 자동으로 가져오지 않도록 하십시오.기본적으로 postinstall 스크립트 비활성화:
.npmrc에ignore-scripts=true를 추가하십시오. 그런 다음package.json에 이 프로젝트가 실제로 필요로 하는 알려진 안전한 라이프사이클 스크립트만 실행하는 명시적인"preinstall"스크립트를 추가하십시오(있는 경우).CI에서 잠금 파일 기반 설치 강제: CI가
npm install이 아닌npm ci를 사용하도록 하십시오. CI 구성 파일이 있으면 확인하고, 없으면 수동 단계로 기록하십시오.증명 확인 추가: CI 파이프라인과 pre-push git 훅에
npm audit signatures단계를 추가하십시오.업데이트 정책 스크립트 생성:
scripts/safe-update.sh를 생성하여 다음을 수행하도록 하십시오:
공급망 인젝션 공격(TanStack/Mini Shai-Hulud 캠페인이
.claude/settings.json및.vscode/tasks.json에 악성 훅을 주입한 것과 같은)에 대비하여 이 프로젝트의 AI 코딩 에이전트 구성을 감사하고 강화하십시오. 다음을 수행하십시오:Claude Code (
.claude/):
.claude/settings.json및.claude/settings.local.json에서 셸 명령을 실행하는hooks항목을 검토하십시오. 특히.claude/,.vscode/또는node_modules/내에서.js,.mjs또는.sh파일을 실행하는 항목을 플래그하십시오.- 합법적인 사용자 생성 목적으로 추적할 수 없는 훅을 제거하십시오.
.claude/settings.local.json이 커밋되지 않도록 규칙을 추가하십시오(로컬로 유지되어야 함).
CI/CD 공급망 공격에 대비하여 이 리포지토리의 GitHub Actions 구성을 감사하고 강화하십시오. 다음을 수행하십시오:
pull_request_target트리거 제거 또는 리팩토링 — 이 트리거는 포크에 의해 트리거되더라도 비밀에 접근할 수 있는 기본 리포지토리 컨텍스트에서 워크플로우 코드를 실행합니다. 필요한 경우pull_request+ 별도의 승인 게이트 워크플로우로 대체하십시오.모든 서드파티 액션을 전체 커밋 SHA로 고정(태그나 브랜치가 아님). 예를 들어
actions/checkout@v4를actions/checkout@<전체-sha>로 바꾸십시오. 가독성을 위해 태그를 주석으로 추가하십시오.모든 워크플로우에 최소 권한 적용. 명시적인
permissions:블록을 추가하십시오. 대부분의 워크플로우는contents: read만 있으면 됩니다. 게시 워크플로우는id-token: write가 필요하며 그 외에는 아무것도 필요하지 않습니다.캐시 범위 제한:
actions/cache를 사용하는 경우 캐시 키가 브랜치 범위로 지정되어 브랜치 간 오염을 방지하도록 하십시오.restore-keys를 신중하게 추가하십시오 — 신뢰할 수 없는 브랜치의 캐시를 복원하지 마십시오.모든 작업의 첫 번째 단계로 StepSecurity Harden-Runner 추가:
step-security/harden-runner@v2를egress-policy: audit(허용된 엔드포인트를 알고 있으면 )와 함께 사용하십시오.
MIT
| # | 확인 항목 | 설명 |
|---|
| 1 | 데드맨 스위치 | 지속성 데몬: gh-token-monitor(1차 물결), kitty-monitor(2차 물결) |
| 2 | 악성 프로세스 | 알려진 공격자 프로세스 이름(두 물결 모두) |
| 3 | 페이로드 파일 | 이름과 SHA-256 해시로 알려진 악성 파일(4개 해시) |
| 4 | Claude Code 훅 | .claude/settings.json에 삽입된 훅 + 일반 SessionStart 휴리스틱 |
| 5 | VS Code 작업 | .vscode/tasks.json에 삽입된 작업 + 일반 runOn: folderOpen 휴리스틱 |
| 6 | GitHub Actions | 모든 워크플로우의 toJSON(secrets) + pull_request_target 경고 |
| 7 | npm 잠금 파일 | 잠금 파일 내 손상된 패키지 버전 |
| 8 | 선택적 종속성 | 악성 @tanstack/setup 및 @antv/setup + 4개의 가짜 커밋 SHA |
| 9 | 네트워크 연결 | C2 인프라에 대한 활성 연결(5개 도메인/IP) |
| 10 | DNS 캐시 | 공격자 도메인의 이전 해상도(4개 도메인) |
| 11 | Git 브랜치 | Dune 테마의 공격자 브랜치 명명 패턴 |
| 12 | 수명 주기 스크립트 | 휴리스틱: preinstall/postinstall의 bun run(설치된 패키지) |
| 13 | 데드 드롭 C2 | git 기록의 firedalazer 키워드 및 "Shai-Hulud" 마커 |
| 14 | GitHub 종속성 | 휴리스틱: optionalDeps에서 커밋 SHA로 고정된 github: 종속성 |
| 패키지 | 악성 버전 | 최초 안전 버전 |
|---|
@tanstack/react-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/router-core | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/vue-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/solid-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/react-start | 1.167.68, 1.167.71 | 1.167.72 |
@tanstack/router-plugin | 1.167.38, 1.167.41 | 1.167.42 |
| 115만 |
npm view <pkg> time --json)npm install <pkg>@latest --save-exact 실행애플리케이션 코드는 변경하지 마십시오. 구성 파일, .npmrc, package.json 스크립트 및 CI 구성만 수정하십시오.
.gitignoreVS Code (.vscode/):
.vscode/tasks.json 및 .vscode/launch.json에서 예상치 못한 스크립트나 바이너리를 실행하는 작업을 검토하십시오.setup.mjs, router_runtime.js 등 이 프로젝트에 속하지 않는 파일을 참조하는 작업 항목을 제거하십시오..vscode/extensions.json에서 인식하지 못하는 확장을 확인하십시오.Cursor (.cursor/):
.cursor/settings.json 및 규칙 파일에서 주입된 명령이나 훅을 검토하십시오..vscode/ 구성을 상속합니다.일반:
package.json에서 직접 작성하지 않은 preinstall, postinstall, prepare 또는 prestart 스크립트를 확인하십시오. 의심스러운 것은 플래그하십시오..github/workflows/에서 pull_request_target을 사용하는 워크플로우를 확인하고, 보안 위험으로 플래그하고 이유를 설명하는 주석을 추가하십시오..gitignore가 자격 증명을 유출할 수 있는 에이전트 세션 파일(.claude/projects/, .cursor/logs/ 등)을 제외하는지 확인하십시오.발견한 사항과 변경한 사항을 보고하십시오. 애플리케이션 코드를 수정하지 마십시오.
block워크플로우 로그에서 비밀 확인: 어떤 워크플로우 단계도 ${{ secrets.* }} 또는 ${{ toJSON(secrets) }}를 stdout으로 출력하지 않는지 확인하십시오.
모든 변경 사항을 보고하십시오. 애플리케이션 코드를 수정하지 마십시오.