활성 Shai-Hulud npm 공급망 공격으로 손상된 Keyv 및 관련 패키지와 자격 증명 탈취 웜의 동작을 분석합니다.
2026년 8월 4일, 공격자들은 주당 약 1억 2,700만 건의 npm 다운로드를 기록하는 키-값 저장소 라이브러리 keyv의 유지관리자 GitHub 계정을 손상시키고, 이 접근 권한을 이용해 전체 패키지 제품군에 자격 증명 탈취 웜을 삽입했습니다. 같은 유지관리자는 cacheable(월간 다운로드 2,900만 건), flat-cache(월간 다운로드 5억 6,500만 건), file-entry-cache(월간 다운로드 5억 5,700만 건)를 비롯한 널리 사용되는 여러 캐싱 유틸리티도 소유하고 있으며, 이들 모두가 동일한 공격에 휩쓸렸습니다.
이번 침해는 악성 파일을 main 브랜치에 직접 푸시한 뒤 즉시 새 릴리스를 만드는 방식으로 수행되었습니다. 즉, 오염된 버전은 GitHub Actions가 서명한 유효한 출처 증명과 함께 npm에 게시되었습니다.
손상된 패키지는 다음과 같습니다:
keyv 6.0.0 (월간 6억 400만 건)flat-cache 6.1.24 (월간 5억 8,000만 건)file-entry-cache 11.1.6 (월간 5억 7,100만 건)cacheable-request 13.0.20 (월간 1억 3,700만 건)cacheable 2.5.1 (월간 3,000만 건)@cacheable/memory 2.2.1 (월간 2,800만 건)cache-manager 7.2.10 (월간 1,600만 건)@cacheable/node-cache 3.1.2 (월간 600만 건)@cacheable/utils 2.5.1 (월간 3,400만 건)@cacheable/net 2.1.1 (월간 3.7천 건)ecto 5.0.1 (월간 4.5천 건)또한 주요 조직을 포함하여, 이 공급망 웜이 다른 유지관리자와 패키지로 매우 활발하게 커뮤니티 확산되는 정황도 확인하고 있습니다.
@deliveroo/reevent 1.0.1@or-sdk/invitations 1.4.9@picsart/ai-sdk 3.32.2@qlik/embed-runtime 1.6.4picasso.js 2.11.6업데이트 — 2026년 8월 4일, 13:37 CEST: 작성 시점에 이 웜으로 최소 434개 패키지(1,381개 버전에 걸쳐)가 손상되었으며, 합산 설치 건수는 월간 20억 건 이상입니다.
이 제품군의 모든 패키지에는 setup.mjs와 Math_Symbol.js라는 두 개의 새 파일이 추가되었고, 각 package.json에는 "preinstall": "node setup.mjs" 항목이 추가되었습니다. 영향을 받는 버전에 대해 npm install을 실행한 사람은 설치가 완료되기 전에 setup.mjs가 자동으로 실행됩니다.
setup.mjs는 강하게 난독화된 드로퍼입니다. 유일한 역할은 github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/에서 Bun JavaScript 런타임을 조용히 내려받아 실제 페이로드인 Math_Symbol.js를 실행하는 것입니다.
execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], {
stdio: 'inherit',
cwd: <script_dir>
})
Math_Symbol.js는 피해자 환경에서 비밀 정보를 수집하고, 수집 결과를 암호화한 뒤, 설명에 Shai-Hulud: Here We Go Again이라고 적힌 공개 GitHub 저장소로 유출하는 자격 증명 탈취기를 포함한 728 KB 크기의 강하게 난독화된 JavaScript 파일입니다. 이 페이로드에는 손상된 패키지 중 하나를 설치한 다른 유지관리자의 패키지를 감염시키기 위한 웜형 전파 기능도 포함되어 있습니다.
Math_Symbol.js 파일은 피해자 시스템의 각기 다른 비밀 저장소를 표적으로 하는 여러 자격 증명 추출기를 구현합니다.
npm 토큰
~/.npmrc를 읽고 파일 시스템에서 다른 모든 .npmrc 파일을 검색합니다. authToken 값과 모든 //registry.*:_authToken=... 항목을 추출합니다. 유출 전에 각 토큰을 registry.npmjs[.]org/-/whoami에 대해 실시간으로 검증합니다.
GitHub 토큰
클래식 PAT(ghp_...)와 OAuth 토큰(gho_...), GitHub App 서버 간 토큰(ghs_...), JWT OIDC 토큰의 세 가지 토큰 형식을 표적으로 합니다. 출처에는 ~/.config/gh/hosts.yml, 환경 변수 및 파일 시스템 검색이 포함됩니다.
GitHub Actions 러너에서는 페이로드가 러너 프로세스 메모리를 직접 읽어 전체 비밀 저장소를 덤프하는 셸 명령도 실행합니다. npm 게시에 사용되는 OIDC 토큰을 탈취하기 위해 ACTIONS_ID_TOKEN_REQUEST_TOKEN 및 ACTIONS_ID_TOKEN_REQUEST_URL을 읽습니다.
AWS 자격 증명
~/.aws/credentials 및 ~/.aws/configAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN 환경 변수169.254.169.254의 EC2 인스턴스 메타데이터 서비스169.254.170.2의 ECS 컨테이너 메타데이터 엔드포인트secretsmanager:ListSecrets를 호출하여 저장된 모든 비밀을 열거하고 유출하는 AWS Secrets ManagerKubernetes 비밀 정보
/var/run/secrets/kubernetes.io/serviceaccount/에서 서비스 계정 토큰, CA 인증서 및 네임스페이스를 읽습니다. 서비스 계정 토큰을 사용하여 Kubernetes API를 직접 쿼리하고 네임스페이스 내의 모든 비밀 정보를 가져옵니다. KUBECONFIG 및 ~/.kube/config도 표적으로 합니다.
HashiCorp Vault 토큰
VAULT_TOKEN 환경 변수, ~/.vault-token, GitHub Actions 러너 경로 /home/runner/.vault-token, 잘 알려진 여러 컨테이너 경로, 탈취한 서비스 계정 JWT를 사용하는 Kubernetes 인증 로그인, 탈취한 AWS 자격 증명을 사용하는 Vault의 AWS IAM 인증 엔드포인트를 우선순위 순으로 6개 출처에서 확인합니다. 토큰을 획득한 후 /v1/sys/mounts를 통해 모든 KV 저장소를 열거하고 KV v1 및 v2 경로의 모든 비밀 정보를 읽습니다.
Stripe 및 Slack 토큰
파일 시스템 스캐너가 접근한 모든 파일에서 Stripe API 키(테스트 및 운영 환경 모두, sk_ 및 pk_ 접두사)와 Slack 토큰(xox[baprs]-...)을 검색합니다.
일반 파일 시스템 검색
플랫폼 인식형 스캐너(macOS 또는 Linux)가 파일 시스템 전반에 걸쳐 약 200개의 글롭 패턴을 실행하며, 특히 다음을 표적으로 합니다.
.env, .env.*, .envrc 파일*.pem, *.key, *.p12, *.pfx, *.jks)id_rsa, id_ed25519, .ssh/config).tfvarsdocker/config.json)*.kdbx)*.ovpn).vscode/tasks.json 및 .claude/settings.json을 포함한 IDE 구성 파일5 MB를 초과하는 파일은 건너뜁니다. 최대 64개의 동시 읽기를 사용합니다. 또한 모든 검색 파일에 일반 정규식 엔진을 적용하여 PEM 개인 키, SSH 공개 키, Azure 스토리지 키, 내장된 자격 증명이 포함된 데이터베이스 연결 문자열, 일반적인 비밀 필드 이름과 일치하는 일반 key=value 패턴을 표시합니다.
자격 증명을 수집하면 페이로드는 전송 전에 전체 묶음을 암호화합니다. 해당 RSA 개인 키를 보유한 공격자만 업로드된 데이터를 복호화할 수 있습니다. 즉, 탈취된 데이터는 공개 인프라에 누구나 볼 수 있는 상태로 존재하지만 다른 누구도 읽을 수 없습니다.
주요 유출 대상은 설명에 Shai-Hulud: Here We Go Again 문자열을 포함하는 공개 GitHub 저장소입니다. 작성 시점에 GitHub에는 이 문자열과 일치하는 공개 저장소가 약 1,300개 있으며, 각각은 피해자의 암호화된 자격 증명 묶음을 위한 수집 지점으로 사용됩니다.

GitHub 업로드에 실패하면 페이로드는 정당한 용도가 없는 것으로 보이는, 2026-05-22에 등록된 도메인인 https://npm-cache[.]com:443/router로 폴백합니다. 이 도메인은 0xE1f2395ee43e45A1556EC6438a88c31B83493103의 Ethereum 스마트 계약에서 동적으로 가져오므로, 공격자는 페이로드를 건드리지 않고도 언제든지 인프라를 교체할 수 있습니다.
자격 증명 탈취를 넘어, 이 페이로드는 탈취한 자격 증명을 적극적으로 사용해 다른 유지관리자와 저장소에 악성코드를 확산합니다. 여기에는 두 가지 고유한 감염 벡터가 있습니다.
npm 타르볼 감염
탈취한 npm 토큰을 사용하여 페이로드는 https://registry.npmjs[.]org/-/npm/v1/tokens를 호출해 해당 토큰이 게시 권한을 가진 모든 패키지를 나열한 다음, 각각의 현재 타르볼을 가져와 압축을 풉니다. 다시 게시하기 전에 다음과 같이 수정합니다.
1.2.3이 1.2.4가 됨)"preinstall": "node setup.mjs"를 추가setup.mjs 및 math_init.js(Math_Symbol.js와 기능적으로 동일)를 패키지에 삽입그런 다음 수정된 타르볼을 다시 패키징하여 레지스트리에 게시합니다.
이것이 웜이 원래 유지관리자 너머로 전파되는 방식입니다. keyv 유지관리자의 최초 침해 이후, 커뮤니티 확산을 통해 400개가 넘는 패키지가 감염된 것을 관찰했습니다. 이러한 2세대 감염은 npm 타르볼 삽입 웜으로 시드되었기 때문에 Math_Symbol.js 대신 math_init.js를 사용한다는 점으로 식별할 수 있습니다.
GitHub 저장소 감염
페이로드가 ghs_ 토큰을 발견하면, 가장 최근에 활성화된 브랜치부터 처리하고 dependabot 및 copilot 브랜치는 건너뛰면서 저장소당 최대 50개 브랜치까지 도달 가능한 모든 브랜치에 커밋합니다. .claude/settings.json 및 .vscode/tasks.json에 악성 훅을 추가하여, 다음에 개발자가 VS Code에서 저장소를 열거나 그 안에서 Claude Code 세션을 시작할 때 npm 설치 없이도 페이로드가 자동 실행되게 합니다. 커밋은 claude@users.noreply.github[.]com 이메일을 사용하는 claude 명의로 작성되며, 실제 커밋에 섞이도록 chore: update config 메시지를 사용합니다.
Aikido 사용자라면 중앙 피드를 확인하고 악성코드 이슈로 필터링하세요. 이 위협은 심각도 100/100 이슈로 표시됩니다. Aikido는 매일 밤 재검사하지만, 지금 수동 재검사를 실행하는 것을 권장합니다.
아직 Aikido 사용자가 아니라면 계정을 만들고 저장소를 연결할 수 있습니다. 악성코드 대응 범위는 무료 요금제에 포함되어 있으며 신용카드는 필요하지 않습니다.
팀 전체에 걸친 더 폭넓은 보호를 위해 Aikido의 Device Protection은 팀 장치에 설치된 소프트웨어 패키지에 대한 가시성과 제어 기능을 제공합니다. 브라우저 확장 프로그램, 코드 라이브러리, IDE 플러그인, 빌드 종속성을 모두 한곳에서 다룹니다. 설치되기 전에 악성코드를 차단하세요.
향후 보호를 위해 Aikido Safe Chain(오픈 소스)을 고려해 보세요. Safe Chain은 기존 워크플로에 배치되어 npm, npx, yarn, pnpm, pnpx 명령을 가로채고, 설치 전에 Aikido Intel을 기준으로 패키지를 검사합니다.
파일
setup.mjs
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668setup.mjs (커뮤니티 확산 버전)
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1ebMath_Symbol.js 및 math_init.js
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc네트워크
https://npm-cache[.]com:443/router — 유출 엔드포인트0xE1f2395ee43e45A1556EC6438a88c31B83493103을 포함하는 eth-mainnet.nodereal[.]io 요청기타
"Shai-Hulud: Here We Go Again"를 포함하는 모든 공개 GitHub 저장소 — 자격 증명 유출에 사용되는 공격자 제어 저장소입니다.