샤이훌루드 재등장: 300개 이상 NPM 패키지 감염
(helixguard.ai)- NPM 레지스트리에서 1,000개 이상의 구성요소가 몇 시간 만에 동일한 방식으로 감염되어 악성 코드가 포함된 새 버전이 배포됨
- 악성 패키지는 Bun 런타임 설치 스크립트로 위장해
setup_bun.js와 난독화된bun_environment.js를 추가, 실행 시 TruffleHog을 이용해 로컬 자격 증명 탈취 - 수집된 AWS/GCP/Azure·GitHub·NPM 토큰 등 민감 정보는
SHA1HULUD라는 GitHub Action 러너를 통해 외부로 전송됨 - 악성 스크립트는
npm publish를 자동 실행해 웜 형태의 자기 복제를 수행, 결과적으로 27,000개 이상의 GitHub 저장소가 감염됨 - 오픈소스 생태계 전반에 걸친 공급망 보안 위협이 다시 부각된 사례로 평가됨
공격 개요
- 2025년 11월 24일 HelixGuard는 NPM 레지스트리에서 1,000개 이상의 패키지가 몇 시간 내 동일한 수법으로 감염된 것을 탐지
- 새 버전들은 Bun 런타임을 추가하는 것처럼 위장하며
preinstall: node setup_bun.js스크립트를 포함 - 함께 배포된
bun_environment.js파일은 난독화된 악성 코드로, 실행 시 TruffleHog을 내려받아 실행함
- 새 버전들은 Bun 런타임을 추가하는 것처럼 위장하며
- TruffleHog은 로컬 환경에서 NPM 토큰, AWS/GCP/Azure 자격 증명, 환경 변수 등을 스캔해 탈취
- 탈취된 정보는 GitHub Action 러너
SHA1HULUD를 생성하고,Sha1-Hulud: The Second Coming.이라는 설명을 가진 GitHub 저장소를 통해 외부로 유출 - HelixGuard는 이 공격이 2025년 9월 발생한 “Shai-Hulud” 사건과 동일 공격자일 가능성을 시사함
악성 코드 동작 분석
- 예시로
@asyncapi/specs패키지를 분석한 결과, NPM에 배포된 버전은 감염되었으나 GitHub 원본 저장소는 안전함 - 공격자는
package.json을 수정해setup_bun.js를 추가하고, 해당 스크립트가bun_environment.js를 호출하도록 구성 -
bun_environment.js는 10MB 이상 크기의 고도 난독화된 자바스크립트 파일로, 주요 기능은 다음과 같음- 환경 변수에서 클라우드 자격 증명 및 토큰 수집
- TruffleHog을 이용한 비밀키 스캔
- GitHub Actions를 통한 데이터 유출
- 또한
package.json을 수정해 감염 코드를 삽입하고,npm publish를 자동 실행하여 웜 형태의 전파를 수행
GitHub 감염 및 데이터 유출
- 악성 스크립트는
.github/workflows/formatter_123456789.yml파일을 생성하고,SHA1HULUD러너를 등록 - 해당 워크플로는 저장소의 비밀키를 이중 Base64 인코딩한
actionsSecrets.json파일로 패키징 - 이후
Sha1-Hulud: The Second Coming.설명을 가진 무작위 이름의 GitHub 저장소를 생성해 데이터를 업로드 - HelixGuard는 27,000개 이상의 GitHub 저장소가 감염된 것으로 확인
- 탈취된 비밀 정보에는
AWS_ACCESS_KEY_ID,SLACK_WEBHOOK_URL,CODECOV_TOKEN,WEBFLOW_TOKEN등 다양한 서비스 자격 증명이 포함됨
감염된 패키지 목록
- HelixGuard는 수백 개의 NPM 패키지가 감염된 것으로 보고
- 대표적으로
@asyncapi,@ensdomains,@posthog,@zapier,@postman,@voiceflow등 주요 조직의 패키지가 포함 - 각 패키지는 다수의 버전(예:
@asyncapi/specs@6.8.2,@postman/csv-parse@4.0.5)이 감염됨
- 대표적으로
- 감염된 패키지들은 대부분 정상적인 오픈소스 프로젝트를 위장하고 있으며, 자동 배포 과정에서 악성 코드가 삽입된 형태
보안적 시사점
- 이번 공격은 공급망 보안의 취약점을 악용해 대규모 오픈소스 생태계를 감염시킨 사례
- NPM·GitHub·클라우드 자격 증명 등 개발 인프라 전반의 보안 관리 강화 필요성이 드러남
- HelixGuard는 감염된 패키지의 설치를 즉시 중단하고, 관련 토큰 및 자격 증명을 즉시 폐기할 것을 권고함
Hacker News 의견
-
팁 하나 공유함: NPM 대신 PNPM을 쓰는 게 좋음
PNPM 10.x는 여러 공격 벡터를 차단함
1️⃣ 기본적으로 post-install 스크립트를 실행하지 않으며, 수동 승인 필요함
2️⃣ 새 릴리스가 배포된 후 일정 기간(예: 4일) 지나야 설치되도록 설정 가능함
NPM은 프로덕션 CLI 환경에서 너무 불안정함
퍼블리셔 키는 최소 권한으로 제한하고, 특정 패키지에만 바인딩하며, IP를 CI/CD 러너에 묶는 게 좋음
로컬에 퍼블리시 키를 두지 말고, 필요하면 OIDC Trusted Publisher나 토큰 기반 접근을 고려함- NPM은 기술 부채가 너무 쌓인 결과물 같음
락파일만 해도 다섯 번은 시도했는데, 여전히 완벽하지 않음
구조와 커밋 히스토리를 보면 팀이 열심히 개선 중이지만, 너무 깊은 구덩이에서 시작한 느낌임
여전히 파일 전송 중 조기 EOF를 감지 못하고, 캐시에 불완전한 파일을 남겨서 느린 인터넷 환경에서는 업데이트 실패로 시간 낭비가 심함 - 나는 HashiCorp Vault / OpenBao의 동적 비밀 관리 방식을 선호함
처음엔 복잡하지만, 비밀을 임대(lease) 개념으로 관리할 수 있음
CI 빌드마다 임대를 생성하고 종료 시 자동 폐기되며, TTL과 자동 회전도 지원함
덕분에 장기 자격 증명을 숨기고, 빌드 시점에만 짧은 수명의 토큰을 발급할 수 있음
이런 공격 덕분에 회사 내에서 진짜 보안 논의가 활발해지는 건 긍정적임 - 단순히
npm ci를 쓰면 됨
package-lock.json에 명시된 버전만 설치하므로 자동 업데이트로 인한 공격 위험을 줄일 수 있음
의도적 업데이트만 수행하는 습관이 중요함 - Python 생태계에서는
pip install --only-binary=:all:옵션으로 비슷한 보호를 얻을 수 있음
소스 배포를 완전히 차단하고 wheel만 설치함
다만 버전 제약이 생길 수 있음
uv에서는--exclude-newer옵션으로 PNPM의 ‘최소 릴리스 기간’ 기능을 흉내낼 수 있음 - 최근 “dependency cooldown” 관련 글을 봤는데, 매우 공감됨
나는 모든 의존성을 고정하고 dependabot 알림을 수동으로 검토함
이게 과한 건지, 올바른 습관인지는 아직 고민 중임
- NPM은 기술 부채가 너무 쌓인 결과물 같음
-
오늘 특히 관련 있는 글이 있음: “We should all be using dependency cooldowns”
자동 의존성 업데이트는 하루짜리 취약점보다 더 위험할 수 있음
이미 수천 개의 lock 파일에 퍼진 감염된 패키지를 되돌리는 게 훨씬 어려움- 아예 필요할 때만 업데이트하는 게 낫다고 생각함
잘 작동한다면 굳이 건드릴 이유가 없음 - 하지만 그렇게 해도 다른 사람이 버그를 잡아줘야 하고, 모두가 쿨다운을 쓰면 결국 제자리걸음일 수 있음
- Python의
uv에서는uv lock --exclude-newer $(date --iso -d "24 hours ago")명령으로 비슷한 효과를 낼 수 있음
관련 논의는 이슈 #14992에 있음 - npm-check-updates로도 간단히 가능함
npx npm-check-updates -c 7명령으로 7일 쿨다운 설정 가능함
npm-check-updates 문서 참고 - 이 논리에 동의하지 않음
쿨다운은 0-day 취약점 확산 시간을 늘릴 수 있음
모두가 같은 쿨다운을 쓰면 발견 지연만 생길 뿐임
- 아예 필요할 때만 업데이트하는 게 낫다고 생각함
-
PostHog 공동 창업자임
이번 공격의 피해자였음
감염된 버전은 posthog-node 4.18.1, 5.13.3, 5.11.3 / posthog-js 1.297.3 / posthog-react-native 4.11.1 / posthog-docusaurus 2.0.6
키와 비밀번호를 모두 교체하고, 새 버전을 배포했음
원인 분석 중이며 status.posthog.com에 업데이트 예정임- 새 패키지 릴리스가 CI/CD 실행과 연관되지 않으면 알람이 울리도록 설정하길 권장함
- 감염된 JS가 실제 사용자에게 영향을 줬는지 궁금함
웹사이트가 감염된 버전을 배포했다면, 방문자에게 피해가 있었는지 알고 싶음 - 원인을 모른다면 공격이 여전히 확산 중일 가능성도 있음
- 최신 버전이 또 감염될 수도 있는데, 왜 사람들이 이번엔 믿어야 하는지 의문임
- 트위터 공지보다 여기 업데이트가 더 눈에 잘 띄어 다행임. 복구 잘 되길 바람
-
진지한 질문임: Node로 새 프로젝트를 시작하는 게 맞는가
Astro로 SaaS 프론트엔드를 만들고 있는데, 의존성 업데이트할 때마다 불안함
npm 생태계의 보안 부재가 너무 심각하게 느껴짐- 문제는 Node나 JS가 아니라 패키징 모델임
Rust처럼 수많은 서브패키지에 의존하는 생태계도 언젠가 같은 일을 겪을 것임
C, C++, Odin처럼 패키지 매니저가 없는 게 오히려 보안상 현명한 선택일 수도 있음 - 나는 Node보다는 npm 자체의 문제라고 생각함
최근엔 Deno의 JSR을 더 신뢰함
JSR 기반 패키지는 npm에도 교차 배포되고, Deno 전용 패키지들도 있음
예를 들어 Lume은 느리지만 안정적인 SSG로 인상 깊었음 - Node만의 문제가 아님
npm이 가장 큰 저장소라 공격자 입장에서 가치가 높을 뿐임
RubyGems나 Cargo에서도 충분히 일어날 수 있음 - Node를 피하자는 의견은 과함
단지 가장 많이 쓰이는 생태계라 공격이 집중되는 것뿐임
의존성을 신중히 관리하고, 매일 업데이트하지 않으면 됨 - 우리는 제품 보안 분석 플랫폼을 PHP로 개발했음
페이지 렌더링에 100개 넘는 의존성이 필요하지 않다는 점이 장점임
프로젝트 링크 참고
- 문제는 Node나 JS가 아니라 패키징 모델임
-
요즘은 모든 개발을 Podman 컨테이너 안에서만 함
읽지 않은 코드는 반드시 격리된 환경에서 실행함
완벽하진 않지만, 최소한의 보안 습관이라 생각함- 대부분의 사람들은 99.99%의 경우 문제가 없으니 위험 감각이 둔함
보안은 보통 전문가에게 위임되는 영역이라, 현실적으로 바꾸기 어려움 - npm 패키지는 의존 트리가 너무 깊은데, 이런 경우 컨테이너 격리가 어떻게 작동하는지 궁금함
- PostHog SDK 같은 npm 패키지를 컨테이너 안에서 어떻게 다루는지 구체적인 방법이 궁금함
- Docker보다 Podman이 더 안전하고, 필요하면 QEMU 같은 추가 격리도 고려할 만함
- 나는 아예 다른 로컬 사용자로 SSH 접속해 tmux로 개발함
- 대부분의 사람들은 99.99%의 경우 문제가 없으니 위험 감각이 둔함
-
12년 전 NPM이 한 번 완전히 다운된 적이 있었음
그때는 단순한 오픈소스 프로젝트였지만, 지금은 Microsoft 소유임
세계 최대 기업 중 하나라면 이런 문제를 해결해야 하지 않겠음?
하지만 여전히 별로 달라진 게 없음- MS는 Windows조차 제대로 관리하지 못함
엔터프라이즈 라이선스로 돈이 안 되는 건 방치함
그래서 Windows 11은 마케팅 덩어리 같음
- MS는 Windows조차 제대로 관리하지 못함
-
우리는 현재 공격 활동을 모니터링 중이며, 감염된 패키지 목록을 Wiz 블로그에 업데이트하고 있음
악성 페이로드를 리버스 엔지니어링 중이며, 몇 시간 내 결과를 공유할 예정임 -
PostHog의 “Talk to a human” 채팅이 실제로는 로봇 응답이라 불편했음
긴급 지원 링크도 제대로 안내되지 않음
그래서 묻고 싶음 — 어떤 버전을 피해야 함?- 공동 창업자임. 메인 스레드와 status.posthog.com에 이미 공지했음
감염된 버전은 posthog-node 4.18.1, 5.13.3, 5.11.3 / posthog-js 1.297.3 / posthog-react-native 4.11.1 / posthog-docusaurus 2.0.6
최신 버전으로 업데이트하면 안전함 - Slack 채널에서도 같은 버전 목록을 공유받았음
- 공동 창업자임. 메인 스레드와 status.posthog.com에 이미 공지했음
-
왜 항상 Node 생태계에서 이런 패키지 혼란이 생기는지 궁금함
왜 이 커뮤니티는 복잡한 설치 훅과 자동 업데이트를 좋은 엔지니어링이라 믿는지 모르겠음
Node 창시자가 이미 떠난 이유가 이해됨- Node는 새로운 PHP 같음
거대한 생태계, 초보 개발자 중심, 보안 의식 부족, 그리고 작은 기능도 라이브러리에 의존함 - 진지한 생태계라면 패키지 메인테이너가 존재해야 함
Debian처럼 신뢰할 수 있는 관리자가 검증해야 하는데, JS 커뮤니티는 이를 게이트키핑이라며 거부함
그래서 이런 사태가 반복됨 - 남을 깎아내려 우월감을 느끼는 건 잠시뿐임
그런 태도로는 아무것도 바뀌지 않음
- Node는 새로운 PHP 같음
-
약간 주제에서 벗어나지만, HelixGuard가 누구인지 궁금함
웹사이트는 엉망이고, 정보도 거의 없음
고객이 암호화폐 거래소라는데 뭔가 수상함- X(트위터) 계정에 따르면 싱가포르/일본 기반임
HelixGuard X 계정
- X(트위터) 계정에 따르면 싱가포르/일본 기반임
2️⃣ 새 릴리스가 배포된 후 일정 기간(예: 4일) 지나야 설치되도록 설정 가능함
너무 좋은 기능이네요. 구글도 종종 버그에 작동 안하는 버전 NPM에 올릴 때 있어서 내 버근가 하고 당황 할 때가 있는데.
실시간 스캐너 스크립트를 만들어보았어요.
의심되는 리포지토리의 path에서
npx sha1-hulud-scanner
입력하시면됩니다.