NOW Briefing
keyv와 cacheable 계열의 메인테이너 계정이 탈취돼 악성 버전이 배포됐습니다. Microsoft가 ChainDrop으로 명명한 이 캠페인은 탈취한 게시 자격증명으로 다른 패키지를 감염시키는 자가전파형 웜입니다.
flat-cache·file-entry-cache는 JavaScript·TypeScript 코드 검사 도구인 ESLint의 전이 의존성으로 설치될 수 있으므로, 8월 4일 전후에 설치·빌드한 환경은 잠금 파일(lockfile)에서 해당 버전 유입 여부를 확인해야 합니다. 감염이 확인되면 패키지 제거보다 자격증명 폐기와 잔존 자동실행 항목 제거가 우선입니다.
keyv·cacheable npm 패키지 침해, 자가전파 웜 ChainDrop 확산
■ 개요
keyv는 여러 데이터 저장 백엔드를 동일한 방식으로 다루는 Node.js 키-값 캐시 라이브러리입니다. cacheable·flat-cache·file-entry-cache는 같은 메인테이너(jaredwray)가 관리하는 캐시 계열 패키지입니다. 이들은 개발자가 직접 설치하지 않아도 ESLint를 비롯한 상위 도구의 전이 의존성(직접 설치한 패키지가 의존하는 하위 패키지)으로 설치될 수 있습니다. 따라서 노출 범위는 특정 서비스가 아니라 Node.js 개발 환경 전반입니다.
2026년 8월 4일 09시(UTC)를 전후해 메인테이너의 GitHub 계정이 탈취되면서 악성 코드가 포함된 버전이 npm에 게시됐습니다. Wiz와 Microsoft는 400개 이상의 패키지가 영향을 받았다고 밝혔고, StepSecurity는 약 4시간 사이에 444개 패키지·2,212개 버전으로 확산된 것으로 집계했습니다. 같은 444개 패키지에 대해 Aikido는 1,381개 버전으로 집계해 버전 수는 출처별로 차이가 있습니다. 영향 패키지의 월간 설치 규모는 Aikido 집계 기준 20억 회를 넘는 것으로 보고됐습니다.
악성 버전을 설치한 환경에서는 클라우드 접근 키, GitHub·npm 토큰, HashiCorp Vault 및 쿠버네티스 서비스 계정 토큰, CI 시크릿(빌드 과정에 저장된 비밀값)이 수집·유출 대상이 됐습니다. 침해는 개발자 워크스테이션에서 끝나지 않고 CI/CD 빌드 실행 서버(러너)를 거쳐 클라우드 계정과 코드 저장소까지 확산될 수 있습니다. 웜이 탈취한 npm 게시 권한으로 다른 조직의 패키지까지 재배포했기 때문에, 최초 피해 저장소와 무관한 조직도 자체 의존성 트리를 점검해야 합니다.
■ 요약
| 항목 | 내용 |
|---|---|
| 캠페인·기법명 | ChainDrop (Shai-Hulud "Mini" 계열 자가전파 웜) |
| 위협 수준 | 심각 — 광범위 전이 의존성 + 자격증명 유출 + 자동 확산 |
| 공격 유형 | 공급망 공격, 계정 탈취 후 악성 버전 배포 설치 전 자동 실행 훅(preinstall) 악용 자격증명 탈취 및 자가전파 |
| 영향/위험 | • 개발자 PC·CI 러너의 클라우드·저장소 자격증명 유출 • 탈취된 npm 토큰으로 타 조직 패키지 2차 감염 • 정상 빌드 출처 증명이 붙어 신뢰 신호가 무력화됨 • 개발 도구 설정 파일에 자동 실행 항목 잔존 |
| 대상 패키지 (초기 확인분) |
• keyv@6.0.0• flat-cache@6.1.24• file-entry-cache@11.1.6 (Socket 집계에는 11.1.7 포함)• cacheable@2.5.1• cacheable-request@13.0.20• cache-manager@7.2.10• @cacheable/net@2.1.1, @cacheable/node-cache@3.1.2, @cacheable/memory@2.2.1, @cacheable/utils@2.5.1, ecto@5.0.1 및 @keyv/*, 2차 감염 패키지 |
| 관측 기간 | 2026-08-04 09:30 UTC 최초 악성 게시(keyv@6.0.0은 09:35 UTC), 같은 날 오후까지 확산. 종료 시점은 출처별로 다름(StepSecurity 09:35~13:20 UTC, Kodem 14:05~15:44 UTC 2차 파동) |
■ 감염 경로와 실행 구조
공격자는 npm 토큰을 직접 탈취한 것이 아니라 메인테이너의 GitHub 계정을 장악했습니다. 악성 코드가 포함된 소스가 저장소 main 브랜치에 커밋된 뒤 정상 릴리스 워크플로가 실행되면서, npm에는 유효한 GitHub Actions 서명과 빌드 출처 증명을 갖춘 악성 패키지가 게시됐습니다. Snyk 분석 기준으로 정상 CI 워크플로가 빌드하기 전에 이미 침해된 소스가 저장소에 커밋돼 있었기 때문에, 정상 파이프라인이 악성 산출물을 그대로 인증했습니다. 유효한 빌드 출처 증명만으로는 안전을 판단할 수 없습니다.
변조된 package.json에서 변경된 부분은 두 곳입니다. 공격자는 패키지 배포물에 포함할 파일 목록(files)에 원본에 없던 스크립트 파일 두 개를 추가했고, 설치 직전에 자동 실행되는 preinstall 훅으로 그중 하나를 실행하도록 등록했습니다. 직전 정상 버전인 keyv@5.6.0은 files 필드 자체를 선언하지 않았고 preinstall 훅도 없었습니다.
/* [악성 주입 - keyv@6.0.0, Socket 인용 스니펫] */
"files": [ "dist", "LICENSE", "setup.mjs", "Math_Symbol.js" ],
"scripts": {
"preinstall": "node setup.mjs"
}
preinstall 훅은 의존성 설치가 끝나기 전에 실행되므로, 사용자가 해당 패키지를 코드에서 한 줄도 호출하지 않아도 npm install 시점에 동작합니다. 약 30KB 크기의 1단계 로더(setup.mjs)는 공식 배포처(github.com/oven-sh/bun/releases)에서 플랫폼에 맞는 자바스크립트 런타임 Bun 1.3.13을 내려받아 실행하는데, 이는 Node.js 프로세스 기준으로 만들어진 기존 모니터링을 우회하는 효과가 있습니다. 2단계는 약 727KB(727,680바이트) 크기의 난독화된 페이로드(Math_Symbol.js)로, 자격증명 수집·유출 경로 결정·게시 서명 생성·패키지 재배포 기능이 모듈로 분리돼 있는 것으로 분석됐습니다.
⚠️ WARN
npm ci로 잠금 파일 기준 설치만 해도 훅은 실행됩니다.
해당 버전이 잠금 파일에 한 번이라도 들어왔다면 현재 설치 여부와 무관하게 실행 이력을 점검해야 합니다.
■ 웜의 자가전파 방식
감염 환경에서 유효한 npm 게시 자격증명을 확보하면, 페이로드는 해당 계정이 게시 권한을 가진 패키지의 최신 배포물을 내려받고 동일한 preinstall 훅과 로더 파일을 추가한 뒤 새 패치 버전으로 재배포합니다. Socket 분석에 따르면 이때 재배포된 패키지에도 새로운 sigstore 증명이 발급됐기 때문에, 2차 감염 패키지도 겉보기에는 정상 게시물과 구분되지 않았습니다. 다만 웜은 소스 커밋이나 풀 리퀘스트 없이 새 패치 버전만 게시하므로, 이러한 릴리스는 주요 식별 신호가 됩니다.
수집 대상은 클라우드 인스턴스 메타데이터 서비스(169.254.169.254, 169.254.170.2)와 자격증명 체인, GCP 서비스 계정 키, Azure 클라이언트 시크릿, Vault 토큰, 쿠버네티스 서비스 계정 토큰, GitHub 조직·저장소 시크릿, npm 토큰으로 폭넓습니다. Wiz 분석에 따르면 이전 변종 대비 탈취 대상이 약 70% 확대됐고, Claude·OpenAI·Codex·Cursor·Gemini 같은 AI 개발 도구 설정과 Foundry·Solana·Monero 키스토어, Jenkins master.key, Argo CD·Harbor 시크릿이 새로 포함됐습니다. CI 러너 메모리에서 마스킹된 시크릿을 평문으로 확보하는 기능도 확인됐으므로, 로그에 시크릿이 보이지 않았더라도 유출된 것으로 간주해야 합니다.
지속성 확보 흔적은 패키지 바깥에도 남습니다. 악성 코드는 사용자 홈 디렉터리에 폴링 스크립트와 토큰 파일, macOS LaunchAgent, Linux systemd 사용자 서비스를 설치합니다. 코드 저장소에는 Claude Code의 세션 시작 훅(.claude/settings.json + .claude/setup.mjs)과 VS Code의 폴더 열기 작업(.vscode/tasks.json + .vscode/setup.mjs)을 커밋해, 감염된 저장소를 클론한 다른 개발자가 편집기로 폴더를 여는 것만으로 재실행되게 만듭니다. 명령·제어(C2) 도메인은 하드코딩 대신 이더리움 스마트 컨트랙트(0xE1f2395ee43e45A1556EC6438a88c31B83493103)를 eth_call로 조회하는 방식이라, 도메인 차단만으로는 통신을 끊기 어렵습니다.
■ 탐지 및 점검
• 잠금 파일과 사설 레지스트리 캐시에서 keyv@6.0.0, flat-cache@6.1.24, file-entry-cache@11.1.6, file-entry-cache@11.1.7, cacheable@2.5.1, cacheable-request@13.0.20, cache-manager@7.2.10 및 @keyv/*·@cacheable/* 8월 4일 배포 버전의 유입 여부를 검색합니다.
• node_modules 전체와 저장소 작업 트리에서 setup.mjs, Math_Symbol.js, math_init.js 파일을 찾고, 발견 시 영향 패키지·설치 경로·생성 시각 등 연관 정보를 함께 확인합니다.
• 설치된 package.json에서 "preinstall": "node setup.mjs" 항목이 있는지 확인합니다.
• 호스트에서 ~/.local/bin/gh-token-monitor.sh, ~/.config/gh-token-monitor/, com.user.gh-token-monitor.plist, gh-token-monitor.service 존재 여부를 점검합니다.
• 저장소에 .claude/settings.json의 SessionStart 훅과 .vscode/tasks.json의 runOn: folderOpen 항목이 커밋 이력에 추가됐는지 확인합니다.
• 프록시·방화벽 로그에서 npm-cache[.]com 통신(:443/router)과 Bun/1.3.13 User-Agent, 빌드 서버의 예상치 못한 이더리움 RPC 엔드포인트 접속을 조회합니다. Wiz가 제시한 eth-mainnet.nodereal.io, go.getblock.io, eth.llamarpc.com은 예시이며, StepSecurity는 공개 RPC 엔드포인트 75곳이 조회 대상이었다고 밝혔습니다.
• 설명이 Shai-Hulud: Here We Go Again인 GitHub 저장소가 해당 계정이나 조직에 생성됐는지 확인합니다.
✅ CHECK
npm 계정에 8월 4일 자신이 게시하지 않은 패치 버전이 올라가 있는지 확인합니다.
GitHub에서는 같은 기간 새로 생성된 저장소와 소스 커밋 없이 발생한 릴리스를 함께 점검합니다.
■ 대응 방안
• 잔존 자동 실행 항목(폴링 스크립트, LaunchAgent, systemd 서비스, 편집기 훅)을 먼저 제거합니다. 자격증명을 먼저 교체하면 새 자격증명이 다시 탈취될 수 있습니다.
• npm 토큰과 GitHub 개인 액세스 토큰은 교체가 아니라 폐기하고, 클라우드 접근 키·Vault 토큰·쿠버네티스 자격증명·CI 시크릿을 모두 깨끗한 호스트에서 재발급합니다.
• package.json의 버전 강제 지정 필드 overrides로 정상 버전(keyv@5.6.0, flat-cache@6.1.23, file-entry-cache@11.1.5)을 고정하고, npm·yarn 캐시를 비운 뒤 npm ci --ignore-scripts로 재설치해 설치 훅 실행을 차단합니다.
• 사설 레지스트리 프록시에서 keyv·@keyv·cacheable 스코프의 8월 4일 이후 신규 버전을 차단하고, 캐시된 악성 배포물을 삭제합니다.
• 파이프라인 표준 설치 옵션을 --ignore-scripts 기준으로 전환하고, 유효한 빌드 출처 증명만으로 패키지를 신뢰하지 않도록 승인 기준을 재정의합니다. Microsoft는 신규 버전 즉시 설치를 막는 npm CLI v12의 설정 항목 min-release-age 적용도 권고했습니다.
💡 TIP
감염 여부가 불확실한 CI 러너는 조사보다 재생성이 빠르고 안전합니다.
일회용 러너를 사용하지 않는다면 러너 수명 정책도 재검토해야 합니다.
[참고 자료]
• Snyk — Inside the keyv npm Compromise: Preinstall Malware, Trusted Provenance, and IDE Hooks
• Wiz — keyv and cacheable npm Package Hijacked in Supply Chain Attack
• Microsoft Security Blog — ChainDrop supply chain compromise: Anatomy of a self-propagating worm
• StepSecurity — ChainDrop npm Worm: Bun-loaded CI/CD credential harvester with Ethereum dead-drop C2
• Aikido — Keyv and friends compromised in npm supply chain attack
• Kodem — keyv npm Supply Chain Attack: Affected Versions, IOCs, and First-Hour Response Runbook



