
keyv와 관련 패키지들이 활발한 Shai-Hulud 공급망 공격에 감염되다
요약
keyv를 포함한 광범위한 npm 패키지군이 Shai-Hulud 공급망 공격에 감염되었습니다. 공격자는 GitHub 계정을 장악해 자격 증명을 탈취하고 웜 형태로 확산되는 악성 코드를 주입했습니다.
핵심 포인트
- keyv, flat-cache 등 월간 수억 건 다운로드되는 패키지 감염
- Bun 런타임을 이용해 난독화된 페이로드를 실행하는 드로퍼 방식
- 환경 변수 및 비밀 정보를 탈취하여 GitHub로 유출
- 감염된 패키지 관리자를 통해 다른 패키지로 확산되는 웜 기능 포함
2026년 8월 4일, 공격자들이 약 1억 2,700만 건의 주간 npm 다운로드를 기록하는 key-value 저장소 라이브러리 keyv를 관리하던 GitHub 계정을 장악했습니다. 그리고 이 접근 권한을 이용해 전체 패키지군에 걸쳐 자격 증명(credential)-탈취 웜(worm)을 주입했습니다. 같은 관리자는 또한 cacheable(월 2,900만 건 다운로드), flat-cache(월 5억 6,500만 건 다운로드), file-entry-cache(월 5억 5,700만 건 다운로드) 및 기타 여러 광범위하게 사용되는 캐싱 유틸리티를 소유하고 있었으며, 이들 모두가 같은 공격에 휘말렸습니다.
이러한 장악은 악성 파일을 main 브랜치에 직접 푸시(push)하고 즉시 새로운 릴리스를 생성함으로써 이루어졌습니다. 이는 오염된 버전들이 GitHub Actions에 의해 유효한 출처 증명(provenance)과 함께 npm에 게시되었음을 의미합니다.
감염된 패키지 목록:
keyv
6.0.0 (월 6억 건)
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.4
picasso.js
2.11.6
업데이트 — 2026년 8월 4일, 13:37 CEST: 이 웜에 의해 최소 434개의 패키지(1,381개 버전)가 감염되었으며, 작성 시점 기준 월간 총 설치 건수는 20억 건이 넘습니다.
무슨 일이 일어났는가
패밀리 내의 모든 패키지는 setup.mjs와 Math_Symbol.js라는 두 개의 새 파일을 받았으며, 각 package.json에 `
is 매우 심하게 난독화된 드로퍼 (dropper)입니다. 이 파일의 유일한 역할은 github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/ 에서 Bun JavaScript 런타임 (runtime)을 조용히 다운로드하고, 이를 사용하여 실제 페이로드 (payload)인 Math_Symbol.js를 실행하는 것입니다.
execFileSync(<bun binary>, ['<script_dir>/Math_Symbol.js'], {
stdio: 'inherit',
cwd: <script_dir>
...
Math_Symbol.js는 피해자의 환경에서 비밀 정보 (secrets)를 수집하고, 수집된 정보를 암호화한 뒤, 설명란에 "Shai-Hulud: Here We Go Again"이라고 적힌 공개 GitHub 저장소로 유출하는 자격 증명 탈취기 (credential stealers)를 포함하고 있는 728 KB 크기의 매우 심하게 난독화된 JavaScript 파일입니다. 또한 이 페이로드에는 감염된 패키지 중 하나를 설치한 다른 관리자의 패키지들을 감염시키기 위한 웜 (worm) 형태의 전파 기능도 포함되어 있습니다.
무엇을 탈취하는가
Math_Symbol.js 파일은 피해자 컴퓨터의 서로 다른 비밀 저장소 (secret store)를 대상으로 하는 일련의 자격 증명 추출기 (credential extractors)를 구현합니다.
npm 토큰 (tokens)
~/.npmrc를 읽고 파일 시스템을 스캔하여 다른 .npmrc 파일들을 찾아냅니다. authToken 값과 //registry.*:_authToken=... 항목들을 추출합니다. 유출하기 전에 registry.npmjs[.]org/-/whoami를 통해 각 토큰이 유효한지 실시간으로 검증합니다.
GitHub 토큰 (tokens)
세 가지 토큰 형식을 대상으로 합니다: 클래식 PAT (ghp_...) 및 OAuth 토큰 (gho_...), GitHub App 서버 간 (server-to-server) 토큰 (ghs_...), 그리고 JWT OIDC 토큰입니다. 소스에는 ~/.config/gh/hosts.yml, 환경 변수 (environment variables), 그리고 파일 시스템 스캔이 포함됩니다.
GitHub Actions 러너 (runners)에서는 페이로드가 셸 명령 (shell command)을 실행하여 러너 프로세스 메모리 (process memory)를 직접 읽어 전체 비밀 저장소를 덤프 (dump)합니다. 또한 npm 배포에 사용되는 OIDC 토큰을 훔치기 위해 ACTIONS_ID_TOKEN_REQUEST_TOKEN 및 ACTIONS_ID_TOKEN_REQUEST_URL을 읽습니다.
AWS 자격 증명 (credentials)
~/.aws/credentials 및 ~/.aws/config를 파싱하며, 모든 명명된 프로필 (named profiles), AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN 환경 변수, 그리고 169.254.169.254에 위치한 EC2 인스턴스 메타데이터 서비스 (EC2 Instance Metadata Service)를 대상으로 합니다.
, 먼저 IMDSv2를 시도한 후 실패할 경우 IMDSv1으로 전환(fallback)을 시도합니다 - 169.254.170.2에 위치한 ECS 컨테이너 메타데이터 엔드포인트 (ECS container metadata endpoint)
- AWS Secrets Manager를 호출하여
secretsmanager:ListSecrets를 실행하고, 여러 리전(region)에 걸쳐 저장된 모든 비밀(secret)을 열거(enumerate) 및 유출(exfiltrate)합니다.
Kubernetes secrets (Kubernetes 비밀)
/var/run/secrets/kubernetes.io/serviceaccount/에서 서비스 어카운트 토큰 (service account token), CA 인증서 (CA certificate), 그리고 네임스페이스 (namespace)를 읽습니다. 서비스 어카운트 토큰을 사용하여 Kubernetes API에 직접 쿼리하고 네임스페이스 내의 모든 비밀을 가져옵니다. 또한 KUBECONFIG 및 ~/.kube/config를 대상으로 합니다.
HashiCorp Vault tokens (HashiCorp Vault 토큰)
다음 여섯 가지 소스를 우선순위에 따라 확인합니다: VAULT_TOKEN 환경 변수 (environment variable), ~/.vault-token, GitHub Actions 러너(runner) 경로인 /home/runner/.vault-token, 몇몇 잘 알려진 컨테이너 경로, 탈취된 서비스 어카운트 JWT를 사용한 Kubernetes 인증 로그인, 그리고 탈취된 AWS 자격 증명 (credentials)을 사용하는 Vault의 AWS IAM 인증 엔드포인트 (auth endpoint). 토큰을 획득한 후에는 /v1/sys/mounts를 통해 모든 KV 스토어 (KV stores)를 열거하고, KV v1 및 v2 경로에서 모든 비밀을 읽습니다.
Stripe and Slack tokens (Stripe 및 Slack 토큰)
파일 시스템 스캐너 (filesystem scanner)가 접근한 모든 파일을 대상으로 Stripe API 키 (sk_ 및 pk_ 접두사를 가진 테스트 및 라이브 키 모두)와 Slack 토큰 (xox[baprs]-...)을 스캔합니다.
Generic filesystem scan (일반 파일 시스템 스캔)
플랫폼을 인식하는 스캐너 (macOS 대 Linux)가 파일 시스템 전체에서 약 200개의 글로브 패턴 (glob patterns)을 실행하며, 다음과 같은 항목들을 대상으로 합니다:
.env,.env.*,.envrc파일- 개인 키 파일 (
*.pem,*.key,*.p12,*.pfx,*.jks) - SSH 키 및 설정 (
id_rsa,id_ed25519,.ssh/config) - Terraform 상태 파일 (state files) 및
.tfvars - Docker 레지스트리 자격 증명 파일 (
docker/config.json) - KeePass 데이터베이스 (
*.kdbx) - VPN 설정 (
*.ovpn) .vscode/tasks.json및.claude/settings.json을 포함한 IDE 설정 파일
5MB를 초과하는 파일은 건너뜁니다. 최대 64개의 동시 읽기(concurrent reads)가 사용됩니다. 또한 모든 스캔된 파일에 대해 일반적인 정규 표현식 엔진 (regex engine)이 적용되어, PEM 개인 키 (PEM private keys), SSH 공개 키 (SSH public keys), Azure 스토리지 키 (Azure storage keys), 자격 증명이 포함된 데이터베이스 연결 문자열 (database connection strings), 그리고 일반적인 비밀 필드 이름과 일치하는 key=value 패턴을 찾아 표시합니다.
데이터 유출 (Exfiltration)
자격 증명 (credentials)이 수집되면, 페이로드 (payload)는 이를 어디론가 전송하기 전에 전체 번들 (bundle)을 암호화합니다. 이에 대응하는 RSA 개인 키 (RSA private key)를 보유한 공격자만이 업로드된 내용을 복호화할 수 있습니다. 이는 도난된 데이터가 공개 인프라 상에 노출되어 있지만, 다른 누구도 읽을 수 없음을 의미합니다.
주요 유출 목적지는 설명란에 "Shai-Hulud: Here We Go Again"이라는 문자열이 포함된 공개 GitHub 리포지토리 (repository)입니다. 이 글을 작성하는 시점을 기준으로, GitHub에는 해당 문자열과 일치하는 약 1,300개의 공개 리포지토리가 있으며, 각 리포지토리는 피해자의 암호화된 자격 증명 번들을 위한 드롭 포인트 (drop point) 역할을 하고 있습니다.

GitHub 업로드에 실패할 경우, 페이로드는 https://npm-cache[.]com:443/router로 폴백 (fallback)합니다. 이 도메인은 2026-05-22에 등록되었으며, 어떠한 정당한 용도로도 사용되지 않는 것으로 보입니다.
Aikido가 이를 탐지하는 방법
Aikido 사용자라면, 중앙 피드 (central feed)를 확인하고 멀웨어 (malware) 이슈로 필터링하십시오. 이는 100/100 점의 심각한 (critical) 이슈로 나타날 것입니다. Aikido는 매일 밤 재스캔을 수행하지만, 지금 즉시 수동 재스캔을 실행할 것을 권장합니다.
아직 Aikido 사용자가 아니라면, 계정을 생성하고 리포지토리를 연결할 수 있습니다. 멀웨어 탐지 기능은 무료 플랜에 포함되어 있으며, 신용카드는 필요하지 않습니다.
팀 전체에 대한 더 넓은 범위를 커버하려면, Aikido의 디바이스 보호 (Device Protection)를 통해 팀의 디바이스에 설치된 소프트웨어 패키지에 대한 가시성과 제어권을 확보할 수 있습니다. 브라우저 확장 프로그램 (browser extensions), 코드 라이브러리 (code libraries), IDE 플러그인 (IDE plugins), 빌드 종속성 (build dependencies)을 한 곳에서 관리하십시오. 멀웨어가 설치되기 전에 차단하세요.
향후 보호를 위해 Aikido Safe Chain (오픈 소스) 사용을 고려해 보십시오. Safe Chain은 기존 워크플로에 위치하여 npm, npx, yarn, pnpm, pnpx 명령어를 가로채고, 설치 전 Aikido Intel을 통해 패키지를 검사합니다.
침해 지표 (IOCs)
파일 (Files)
setup.mjs
-
SHA-256
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668 -
SHA-256
setup.mjs
(커뮤니티 확산 버전 (community-spread version)) - SHA-256
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
- SHA-256
Math_Symbol.js
및 math_init.js
-
SHA-256
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc -
SHA-256
네트워크 (Network)
https://npm-cache[.]com:443/router
— 데이터 유출 엔드포인트 (exfiltration endpoint)
기타 (Other)
- 설명에 `
AI 자동 생성 콘텐츠
본 콘텐츠는 HN AI Posts의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기