DEEP DIVE REPORT

ChainDrop 대응 가이드: 440개 npm 패키지와 자격증명 기반 자기전파 차단

SecurityDesk
2026.08.06 조회 9

서론

2026년 8월 4일, npm 생태계에서 대규모 공급망 공격이 발생했다. SecurityWeek는 이 공격을 'ChainDrop'으로 명명했으며, Mini Shai-Hulud 악성코드의 변종이 440개 이상의 npm 패키지에 2,200개 이상의 악성 버전을 퍼뜨린 것으로 확인되었다. 공격의 시작점은 keyvcacheable 네임스페이스 패키지의 유지관리자 GitHub 계정 탈취였으며, 이후 탈취된 npm 게시 토큰을 통해 영향 범위가 기하급수적으로 확대되었다.

이 공격이 주목받는 이유는 단순한 패키지 오염을 넘어 '자기전파(self-propagating)' 웜 행동을 보였기 때문이다. 악성코드는 감염된 시스템에서 npm, GitHub, AWS, Kubernetes, HashiCorp Vault 등의 자격증명을 수집한 뒤, 탈취한 자격증명을 이용해 추가 패키지를 오염 게시하고 GitHub 저장소에도 악성 설정 파일을 주입하는 사슬 구조를 형성했다. 또한 이더리움 블록체인 스마트 계약을 명령 및 통제(C2) 채널에 활용한 'EtherHiding' 기법은 이전 공급망 공격에서 본 적 없는 새로운 시도였다.

이 글은 ChainDrop 공격의 전모를 기술적으로 분석하고, 조직이 즉각 취해야 할 대응 조치와 장기적 공급망 보안 강화를 위한 전략을 제시한다.

본론

영향 범위

ChainDrop 공격의 영향은 단순히 특정 패키지의 사용자가 아닌, npm 생태계 전반에 걸쳐 확산되었다.

직접 영향 패키지

공격이 시작된 keyvcacheable 네임스페이스 패키지는 npm 생태계에서 가장 기본적이고 널리 사용되는 캐싱 라이브러리이다. keyv는 Redis, SQLite, Postgres, MongoDB 등 다양한 어댑터를 지원하는 키-값 저장소 추상화 계층이며, cacheable 계열은 HTTP 요청 캐싱, 파일 캐싱 등의 기능을 제공한다. 중요한 점은 이 패키지들이 직접 설치되기보다 eslintfile-entry-cacheflat-cachekeyv와 같은 전이 의존성(transitive dependency) 체인의 일부로 수십억 설치에서 사용된다는 사실이다.

Microsoft는 영향을 받은 440개 이상 패키지가 결합적으로 주 5억 회 이상 다운로드되며, 이 패키지들의 오염으로 433개의 추가 패키지가 감염되었다고 보고했다. StepSecurity의 분석에 따르면 8월 4일 4시간 미만 동안 2,212개의 악성 패키지 버전이 게시되었다.

간접 영향 범위

악성코드가 탈취한 npm 게시 토큰을 통해 게시 가능한(package write permission) 모든 패키지가 오염 대상으로 포함되었다. 즉, 한 명의 유지관리자 토큰 탈취가 해당 유지관리자가 관리하는 모든 패키지로 즉각 확장된다. 또한 GitHub 자격증명 탈취를 통해 접근 가능한 저장소에도 악성 설정 파일이 주입되어, 저장소를 클론하는 개발자까지 감염 경로가 확대된다.

고위험 환경

  • 개발자 작업 환경: npm install 시 preinstall 훅 자동 실행
  • CI/CD 빌드 러너: 워크플로우 시크릿, OIDC 게시 권한 노출
  • 아티팩트 저장소: 오염된 tarball 캐싱으로 2차 확산
  • 공유 베이스 이미지: golden build runner 재사용 시 지속 감염

기술적 분석

ChainDrop 공격은 여러 단계로 구성되며, 각 단계가 다음 단계의 실행 조건을 만든다.

1단계: 초기 진입과 패키지 오염

공격자는 keyvcacheable 네임스페이스의 유지관리자 계정(Jaredwray)의 GitHub 자격증명을 탈취한 것으로 추정된다. 이를 통해 원본 패키지의 소스코드는 변경하지 않고, package.json에 preinstall 라이프사이클 훅과 두 개의 악성 파일을 추가한 tarball을 직접 npm 레지스트리에 게시했다.

"files": [ "dist", "LICENSE", "setup.mjs", "Math_Symbol.js" ],
"scripts": {
  "preinstall": "node setup.mjs"
}

원본 dist/ 파일의 SHA-256 해시는 깨끗한 6.0.0-rc.1 빌드와 완전히 동일했으며, 모든 악의적 행동은 추가된 preinstall 훅에 담겼다. 즉, 패키지가 정상적으로 설치된 후엔 아무런 이상 없이 동작하되, 설치 과정에서 호스트 시스템이 이미 감염되는 구조이다.

2단계: Bun 로더 (setup.mjs)

preinstall 훅은 Node.js로 실행되는 setup.mjs를 호출한다. 이 로더는 약 710KB의 두 번째 스테이지 페이로드를 실행하기 위해 Bun JavaScript 런타임을 사용한다. 시스템에 Bun이 없으면 GitHub에서 Bun 1.3.13를 다운로드해 추출한 뒤 페이로드를 실행하고, 임시 디렉토리를 삭제해 디스크 아티팩트를 최소화한다.

const V = "1.3.13";
const url = "https://github.com/oven-sh/bun/releases/download/bun-v" + V + "/" + target + ".zip";

Bun을 통해 실행됨으로써 Node.js 프로세스만 모니터링하는 보안 도구를 우회할 수 있다.

3단계: 메인 페이로드 (Math_Symbol.js)

약 728KB의 Bun 번들로, 문자열은 다형성 basE91 인코딩으로 보호되어 분석을 극도로 어렵게 만든다. 내부 모듈 로그 태그는 [collector], [dispatcher], [provenance], [publish]로 구성되며, 각 모듈이 명확한 역할을 담당한다.

자격증명 수집 (collector)

페이로드는 다음 범위의 자격증명을 표적한다:

  • 패키지 매니저 토큰: npm, Yarn, PyPI, Verdaccio
  • 클라우드 자격증명: AWS, Azure, GCP, Alibaba, Tencent, Hetzner
  • 인프라 시크릿: Kubernetes 서비스 계정 토큰, HashiCorp Vault KV 마운트
  • GitHub 자격증명: PAT(Personal Access Token), OAuth 토큰, CLI 토큰
  • CI/CD 시크릿: GitHub Actions 러너 메모리에서 시크릿 추출
  • 로컬 파일: SSH 키, .env 파일, 셸 히스토리, 브라우저 자격증명 저장소
  • AI 도구 자격증명: OpenAI, Anthropic, Claude, Cursor, Codex, Gemini 등

특히 Linux GitHub Actions 러너에서는 sudo python3를 통해 /proc/<pid>/mem을 읽어 Runner.Worker 프로세스 메모리에서 isSecret:true로 표시된 시크릿 스토어 객체를 직접 추출한다.

자격증명 열거 및 검증 (dispatcher)

수집된 자격증명으로 실제 서비스 API를 호출해 접근 권한을 확인하고 추가 시크릿을 열거한다. AWS 자격증명의 경우 IMDSv2, ECS 메타데이터, 웹 인증 토큰 등 다양한 경로를 통해 해결한 뒤, 17개 리전에서 Secrets Manager와 SSM Parameter Store를 열거한다.

4단계: 명령 및 통제 (EtherHiding)

이전 Shai-Hulud 공격과 가장 큰 차이는 C2 채널이다. ChainDrop은 이더리움 메인넷 스마트 계약(0xE1f2395ee43e45A1556EC6438a88c31B83493103)을 통해 C2 도메인 주소를 동적으로 해석한다. eth_call로 선택자 0x53ed5143을 호출하면 응답이 C2 도메인 목록으로 디코딩되며, 현재 활성 도메인은 npm-cache[.]com이다. 이전 후보 도메인으로는 pypi-get[.]com, js-mirror[.]com이 확인되었다.

스마트 계약 접근이 실패하면 GitHub 커밋 메시지에서 마커 thebeautifulmarchoftime을 검색하고, RSA-4096 공개 키로 서명 검증된 폴백 페이로드를 실행한다.

5단계: 데이터 유출

수집된 데이터는 JSON 직렬화 → gzip 압축 → AES-256-GCM 암호화(랜덤 32바이트 키 + 12바이트 IV) → RSA-OAEP-SHA256 키 암호화의 과정을 거쳐 HTTPS 채널을 통해 전달된다. C2 서버 응답의 code 필드가 있으면 이를 eval에 전달해 원격 코드 실행(RCE)이 가능하다.

직접 C2가 불가능할 경우 탈취된 GitHub 토큰으로 공개 저장소를 생성하고, 저장소 설명에 Shai-Hulud: Here We Go Again을 포함해 암호화된 결과를 커밋한다. 더 나아가 피해자 계정 조직이 없을 경우 피해자 자신의 PAT를 이중 Base64 인코딩해 커밋 메시지에 게시하며, 다른 감염 호스트가 이 토큰을 검색해 재사용할 수 있는 분산 폴백 채널을 형성한다.

6단계: 자기전파 (publish)

탈취한 npm 토큰이 패키지 게시 권한(bypass_2fa === true + package write)을 가지면 웜 전파 루틴이 실행된다:

  1. 토큰으로 게시 가능한 모든 패키지 열거
  2. 각 패키지의 최신 tarball 다운로드
  3. 악성코드를 math_init.js로 복사, 로더를 setup.mjs로 쓰기
  4. 라이프사이클 스크립트 교체 → scripts.preinstall 설정
  5. 패치 버전 증가 후 다시 게시

이 과정을 통해 하나의 탈취된 토큰이 해당 게시자가 관리하는 모든 패키지에 악성 패치 버전을 퍼뜨린다. Microsoft는 이 과정에서 GitHub Actions OIDC 게시 접근 권한도 악용되어, 합법적인 워크플로우 아이덴티티에서 유효한 provenance를 가진 악성 tarball이 게시될 수 있다고 확인했다.

7단계: 저장소 영속성

탈취된 GitHub 자격증명으로 저장소 브랜치에 5개의 파일을 커밋한다:

  • .vscode/tasks.json — 환경 설정 태스크 정의
  • .vscode/setup.mjs — Bun 부트스트랩 로더
  • .claude/math_init.js — 웜 페이로드
  • .claude/settings.json — 세션 시작 훅
  • .claude/setup.mjs — Bun 부트스트랩 로더

.vscode/tasks.json은 폴더 열림 시 node .claude/setup.mjs를 실행하고, .claude/settings.json은 SessionStart 훅으로 node .vscode/setup.mjs를 실행하도록 설정된다. 감염된 저장소를 VS Code로 열거나 Claude 세션을 시작하는 것만으로도 페이로드가 재실행된다.

커밋은 GitHub GraphQL createCommitOnBranch 뮤테이션을 통해 저장소당 최대 50개 브랜치에 푸시되며, 커밋 메시지는 chore: update config, 작성자는 claude <claude@users.noreply.github.com>로 위장된다.

8단계: GitHub Actions 시크릿 추출

ghp_ 또는 gho_ 토큰에 workflow 스코프가 있으면 더 표적화된 공격이 진행된다:

  1. 2025년 9월 이후 푸시된 최대 100개 저장소 열거
  2. Actions 시크릿 존재 여부 확인
  3. dependabot/github_actions/format/setup-formatter 브랜치 생성
  4. .github/workflows/codeql_analysis.yml 워크플로우 주입
  5. 워크플로우가 ${{ toJSON(secrets) }}를 환경 변수에 할당하고 format-results.txt로 쓰기
  6. 아티팩트 다운로드 → 시크릿 추출
  7. 워크플로우 실행 기록 및 임시 브랜치 삭제

9단계: 자살 스위치

macOS와 Linux에서 영속성 달성 후, 악성코드는 60초마다 GitHub API를 폴링해 탈취한 토큰이 아직 유효한지 확인한다. 토큰이 작동하지 않으면 상태를 삭제하고 자체 종료된다. 또한 24시간 후에도 자체 정리된다. 추가적으로 gh-token-monitor 컴포넌트는 macOS LaunchAgents와 Linux user systemd에 설치될 수 있으며, 모니터링 중인 토큰이 폐지되면 파괴 핸들러를 실행한다.

공격 시나리오

유지관리자 GitHub 계정 탈취
    ↓
keyv, cacheable 패키지 preinstall 훅 추가 게시
    ↓
개발자/CI/CD에서 npm install 실행
    ↓
setup.mjs 실행 → Bun 다운로드 → Math_Symbol.js 실행
    ↓
로컬/클라우드/CI 자격증명 수집
    ↓
이더리움 스마트 계약 → C2 도메인 해석 → 암호화 후 유출
    ↓
탈취된 npm 토큰으로 추가 패키지 오염 게시
탈취된 GitHub 토큰으로 저장소 설정 파일 주입
    ↓
(반복) 추가 시스템 감염 → 추가 자격증명 수집 → 추가 전파

IOC (감시 지표)

지표 설명
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668 setup.mjs (npm tarball preinstall 로더) SHA-256
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb setup.mjs (.claude/.vscode 저장소 로더) SHA-256
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc Math_Symbol.js 메인 페이로드 SHA-256
npm-cache[.]com C2 도메인
pypi-get[.]com C2 도메인 (이전)
js-mirror[.]com C2 도메인 (이전)
https://npm-cache[.]com:443/router C2 엔드포인트
0xE1f2395ee43e45A1556EC6438a88c31B83493103 이더리움 C2 스마트 계약
Shai-Hulud: Here We Go Again GitHub 폴백 저장소 설명
thebeautifulmarchoftime GitHub 커밋 마커
chore: update config 악성 커밋 메시지
dependabot/github_actions/format/setup-formatter 악성 브랜치명

탐지 시그널

Microsoft Defender XDR에서 제공하는 탐지 목록:

  • Trojan:NPM/ShaiLoader.BY — preinstall 훅을 통한 페이로드 실행
  • Trojan:NPM/MalBun.A — Bun 기반 악성코드
  • Trojan:NPM/ShaiWorm.DAY!MTB — 자기전파 웜
  • Behavior:Linux/SuspBunActivity.A — Linux Suspicious Bun Activity
  • Behavior:Win32/SuspBunActivity.A — Windows Suspicious Bun Activity
  • Suspicious npm supply-chain compromise activity detected — Microsoft Defender for Cloud

대응 방안

즉시 대응 (24시간 이내)

  1. 영향 패키지 확인: 프로젝트의 package-lock.json, yarn.lock, CI 캐시에서 keyv, cacheable, flat-cache, cacheable-request, file-entry-cache, cache-manager 및 관련 @keyv/*, @cacheable/* 스코프 패키지의 버전을 확인한다. 오염된 버전 목록은 Socket과 JFrog의 캠페인 페이지에서 확인 가능하다.
  2. 시스템 격리: 오염된 버전을 설치한 개발자 환경과 CI 러너를 네트워크에서 격리한다. 정리 전까지 패키지 tarball, npm 로그, CI 로그, GitHub 감사 로그, 러너 이미지를 보존한다.
  3. 자격증명 폐지 및 교체: 영향을 받은 계정과 CI 작업의 모든 npm 토큰을 폐지한다. 특히 bypass_2fa와 게시 권한이 있는 토큰부터 우선 처리한다. GitHub PAT, OAuth 토큰, Actions 토큰, AWS, Azure, GCP, Kubernetes, Vault, SSH, VPN, AI 서비스 자격증명도 모두 교체한다.
  4. npm CLI v12 이상으로 업그레이드: npm 12 이상에서는 preinstall 라이프사이클 훅이 기본으로 실행되지 않아 이 공격의 페이로드가 실행되지 않는다. 또한 min-release-age 설정을 활용해 최근 게시된 패키지의 설치를 지연시킨다.
  5. 캐시 및 node_modules 정리: npm과 yarn 캐시를 삭제하고, 기존 node_modules 트리를 제거한다. 공유 CI 캐시에 오염된 tarball이 기록되었을 가능성을 특히 주의한다.

단기 대응 (1주 이내)

  1. 지정 버전 고정 (pinning): 알려진 안전한 패키지 버전을 package.json에 고정한다.
  2. GitHub 저장소 감사: 모든 브랜치(디폴트 브랜치뿐 아니라 모든 브랜치)에서 다음 5개 파일의 존재를 확인한다.
  3. .vscode/tasks.json, .vscode/setup.mjs
  4. .claude/math_init.js, .claude/settings.json, .claude/setup.mjs
  5. GitHub 감사 로그 검토: chore: update config 커밋 메시지, dependabot/github_actions/format/setup-formatter 브랜치, codeql_analysis.yml 워크플로우 생성/삭제, format-results 아티팩트 생성 기록을 확인한다.
  6. CI 러너 재빌드: 오염된 CI 러너와 빌드 머신은 정리하는 것이 아닌 깨끗한 이미지로 재빌드한다. 공유 베이스 이미지나 golden build runner도 함께 재빌드한다.
  7. OIDC 게시 워크플로우 검토: GitHub Actions에서 npm OIDC 게시를 사용하는 워크플로우의 토큰 스코프, 워크플로우 승인, 보호 환경, 게시 provenance, 자동 패키지 게시 관련 이상 징후를 검토한다.

장기 대응 (1개월 이내)

  1. 의존성 모니터링 도입: JFrog Curation, Socket, Snyk 등 공급망 보안 도구를 도입해 악성 패키지를 사전에 차단한다. JFrog Xray의 레이블 영향 검색(Label Impact Search)으로 Shai-Hulud: Cacheable and Keyv 레이블을 검색해 영향 여부를 확인할 수 있다.
  2. 라이프사이클 스크립트 정책: 조직 내 npm/yarn 라이프사이클 스크립트 실행 정책을 마련하고, preinstall 훅이 포함된 패키지에 대한 경고를 설정한다.
  3. 시크릿 관리 강화: 자격증명을 파일 기반에서 시크릿 관리 서비스(Vault, AWS Secrets Manager 등)로 이전하고, 최소 권한 원칙을 적용한다. GitHub Actions 시크릿의 접근 범위를 최소화한다.
  4. 보완적 검증: 복구 후 알려진 안전한 의존성 베이스라인에서 프로젝트를 재빌드하고, 오염된 해시가 패키지 캐시나 아티팩트 저장소에 남아있지 않은지 확인한다. 엔드포인트 텔러미터에서 Math_Symbol.js, Math_init.js, 또는 math_<guid>.js와 유사한 이름의 Node.js 디렉토리 아티팩트와 의심스러운 node 자식 프로세스를 검토한다.
  5. 개발 환경 보안: .claude, .vscode 설정 파일의 변경을 저장소 정책과 코드 리뷰로 통제하고, 알 수 없는 설정 파일 자동 실행을 방지한다.

결론

ChainDrop 공격은 단순한 패키지 오염을 넘어, 자격증명 수집 → 자기전파 → 저장소 영속성 → C2 기반 지휘통제까지 완비된 공급망 웜 공격이다. npm 생태계에서 가장 널리 사용되는 캐싱 라이브러리를 표적으로 선정한 것은 전이 의존성 체인을 통해 최대한 넓은 영향 범위를 확보하려는 의도로 해석된다.

특히 이더리움 스마트 계약을 C2 채널에 활용한 EtherHiding 기법, GitHub 커밋 기반 분산 폴백 채널, GitHub Actions OIDC 게시 악용, Claude/VS Code 설정 파일을 통한 개발자 간 2차 전파 등 여러 새로운 기술이 적용된 점에 주목해야 한다.

조직은 즉각적인 영향 확인과 자격증명 교체를 우선으로 하고, npm CLI 업그레이드와 의존성 모니터링 도구 도입으로 장기적 방어 체계를 강화해야 한다. 공급망 보안 대응은 호스트 트리아지에서 끝나면 안 되며, 릴리스 프로세스 자체의 무결성 검증까지 포함되어야 한다.

참고문헌

  1. SecurityWeek — Over 400 NPM Packages Infected in ChainDrop Supply Chain Attack
    https://www.securityweek.com/over-400-npm-packages-infected-in-chaindrop-supply-chain-attack/

  2. Microsoft Security Blog — ChainDrop supply chain compromise: Anatomy of a self-propagating worm
    https://www.microsoft.com/en-us/security/blog/2026/08/04/chaindrop-supply-chain-compromise-anatomy-self-propagating-worm/

  3. JFrog Research — Major Shai Hulud campaign strikes npm again, affecting keyv and 400+ packages
    https://research.jfrog.com/post/shai-hulud-is-back-august/

  4. Socket — Popular npm Packages in the keyv and Cacheable Namespaces Compromised in Active Supply Chain Attack
    https://socket.dev/blog/popular-npm-packages-in-the-keyv-and-cacheable-namespaces-compromised-in-active-supply-chain

  5. StepSecurity — ChainDrop npm Worm
    https://www.stepsecurity.io/blog/chaindrop-npm-worm

  6. GitHub Changelog — Upcoming breaking changes for npm v12
    https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/

  7. npm Documentation — min-release-age configuration
    https://docs.npmjs.com/cli/v11/using-npm/config


본 콘텐츠는 AI 기술로 작성된 분석 리포트를 포함하고 있습니다. 내용 중 사실과 다르거나 보완이 필요한 정보를 발견하셨으면 댓글을 통해 의견을 부탁드립니다. 여러분의 피드백은 더 정확한 보안 정보 공유에 큰 도움이 됩니다.

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

로그인

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!

IT 도구 서랍

→ Unix: 2025-01-15T09:30:00
→ 날짜: 1736934600

→ ASCII: ABC
→ 문자: 65 66 67

ASCII 코드표 — 클릭하면 입력란에 추가

DecHex약어설명
DecHex문자
DecHex문자

→ 유니코드: 홍길동
→ 문자: \ud64d\uae38\ub3d9