# Shai-Hulud가 개발자 머신을 감염시켜 GitHub 조직 접근권을 탈취한 사건: 사후 분석

> Clean Markdown view of GeekNews topic #25092. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=25092](https://news.hada.io/topic?id=25092)
- GeekNews Markdown: [https://news.hada.io/topic/25092.md](https://news.hada.io/topic/25092.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-12-15T17:33:19+09:00
- Updated: 2025-12-15T17:33:19+09:00
- Original source: [trigger.dev](https://trigger.dev/blog/shai-hulud-postmortem)
- Points: 4
- Comments: 3

## Topic Body

- **Shai-Hulud 2.0** 악성 npm 패키지가 개발자 머신을 감염시켜 **Trigger.dev의 GitHub 조직 접근권**을 탈취한 사건 발생  
- 감염은 개발자가 `pnpm install` 실행 시 악성 패키지의 **preinstall 스크립트**가 실행되며 시작, **TruffleHog** 도구를 이용해 자격 증명 탈취  
- 공격자는 17시간 동안 **669개 저장소를 복제**하고, 이후 10분간 **199개 브랜치에 강제 푸시 및 42개 PR 폐쇄** 시도  
- **패키지와 프로덕션 시스템은 손상되지 않았으며**, 공격은 4분 만에 탐지되어 계정 접근이 차단됨  
- 사건 이후 npm 스크립트 비활성화, **pnpm 10 업그레이드**, **OIDC 기반 npm 배포**, **브랜치 보호 전면 적용** 등 보안 체계 강화  

---

### 공격 개요
- 2025년 11월 25일, 내부 Slack 디버깅 중 **Linus Torvalds 명의의 “init” 커밋**이 여러 저장소에 생성되는 이상 징후 발생  
- 조사 결과, **Shai-Hulud 2.0** 공급망 웜이 개발자 머신을 감염시켜 GitHub 자격 증명을 탈취한 것으로 확인  
- 이 웜은 500개 이상 npm 패키지를 감염시키고, 25,000개 이상의 저장소에 영향을 미친 것으로 보고됨  
- Trigger.dev의 공식 npm 패키지(`@trigger.dev/*`, CLI)는 감염되지 않음  

### 공격 타임라인
- 11월 24일 04:11 UTC: 악성 패키지 배포 시작  
- 20:27 UTC: 독일의 개발자 머신 감염  
- 22:36 UTC: 공격자 첫 접근 및 대량 저장소 복제 시작  
- 15:27~15:37 UTC (11월 25일): 10분간 파괴적 공격 수행  
- 15:32 UTC: 이상 탐지 및 4분 내 접근 차단  
- 22:35 UTC: 모든 브랜치 복구 완료  

### 감염 과정
- 개발자가 `pnpm install` 실행 시, 악성 패키지의 **preinstall 스크립트**가 실행되어 TruffleHog을 다운로드 및 실행  
- TruffleHog은 **GitHub 토큰, AWS 자격 증명, npm 토큰, 환경 변수** 등을 스캔 후 외부로 유출  
- 감염된 머신에서 `.trufflehog-cache` 디렉터리와 관련 파일이 발견됨  
- 감염 원인 패키지는 삭제되어 추적 불가  

### 공격자의 활동
- 감염 후 17시간 동안 **정찰 활동** 지속  
  - 미국과 인도 기반 인프라를 이용해 669개 저장소 복제  
  - 개발자 활동을 모니터링하며 GitHub 토큰을 이용한 접근 유지  
  - “Sha1-Hulud: The Second Coming”이라는 이름의 저장소 생성, 자격 증명 저장에 사용된 것으로 추정  
- 이후 10분간 **파괴적 행위** 수행  
  - 16개 저장소에서 199개 브랜치 강제 푸시 시도  
  - 42개 PR 폐쇄, 일부는 브랜치 보호 설정으로 차단  
  - 모든 커밋은 “Linus Torvalds &lt;email&gt; / init” 형태로 표시  

### 탐지 및 대응
- Slack 알림을 통해 이상 징후를 실시간 탐지  
- 4분 내 감염 계정의 GitHub 접근 차단, 이후 AWS·Vercel·Cloudflare 등 모든 서비스 접근 철회  
- AWS CloudTrail 로그 분석 결과, **읽기 전용 API 호출만 존재**, 프로덕션 데이터 접근 없음  
- AWS는 별도로 Shai-Hulud 관련 의심 행위를 탐지해 경고 발송  

### 피해 및 복구
- **669개 저장소 복제**, **199개 브랜치 강제 푸시**, **42개 PR 폐쇄**  
- GitHub의 서버 측 reflog 부재로 복구 어려웠으나, **이벤트 API와 로컬 reflog**를 활용해 7시간 내 전체 복원  
- npm 패키지 및 프로덕션 인프라는 손상되지 않음  

### GitHub App 키 노출
- 조사 중 개발자 노트북의 휴지통에서 **GitHub App 개인 키** 발견  
- 해당 키는 고객 저장소에 **read/write 권한**을 가지며, 즉시 회전 조치  
- 데이터베이스(설치 ID 저장)는 손상되지 않아 **고객 저장소 접근 증거 없음**, 그러나 완전 배제는 불가  
- GitHub 지원팀에 추가 로그 요청 및 고객에게 이메일 공지 발송  

### Shai-Hulud 기술 분석
- `setup_bun.js` 실행 시 **Bun 런타임**을 설치하고 백그라운드에서 `bun_environment.js` 실행  
- TruffleHog을 이용해 `$HOME` 디렉터리 내 자격 증명 수집  
- 수집된 데이터(`contents.json`, `cloud.json`, `truffleSecrets.json` 등)를 **랜덤 GitHub 저장소에 3중 base64 인코딩** 형태로 업로드  
- npm 토큰이 존재할 경우, 감염된 계정의 패키지를 수정·재배포하여 웜 확산  
- 자격 증명이 없을 경우 **홈 디렉터리 삭제 시도**  
- 감염 지표 파일: `setup_bun.js`, `bun_environment.js`, `.trufflehog-cache/` 등  

### 보안 강화 조치
- **npm 스크립트 전면 비활성화** (`ignore-scripts=true`)  
- **pnpm 10 업그레이드**: 스크립트 기본 비활성화, `minimumReleaseAge`(3일) 설정으로 신규 패키지 설치 지연  
- **OIDC 기반 npm Trusted Publishers** 도입으로 장기 토큰 제거  
- **모든 저장소에 브랜치 보호 적용**  
- **AWS SSO에 Granted 도입**, 세션 토큰 암호화  
- **GitHub Actions**에서 외부 기여자 워크플로 실행 시 승인 필수로 변경  

### 다른 팀을 위한 교훈
- npm 설치 시 실행되는 **임의 코드 실행 구조 자체가 공격 표면**  
- `ignore-scripts=true` 설정 및 필요한 패키지만 화이트리스트 관리 필요  
- **pnpm minimumReleaseAge**로 신규 패키지 설치 지연  
- **브랜치 보호**와 **OIDC 기반 배포**는 필수 보안 조치  
- 로컬 머신에 장기 자격 증명 저장 금지, CI를 통한 배포만 허용  
- **Slack 알림의 소음이 탐지의 열쇠**가 되었음  

### 인간적 측면
- 감염된 개발자는 잘못이 없으며, 단순히 `npm install` 실행만으로 피해 발생  
- 공격 중 해당 계정이 수백 개의 무작위 저장소를 자동으로 ‘star’한 흔적 발견  
- 사건은 개인의 실수가 아닌 **생태계의 구조적 취약성**을 드러냄  

### 요약 지표
- 최초 감염 후 첫 공격까지: 약 2시간  
- 공격자 접근 유지 시간: 17시간  
- 파괴 행위 지속 시간: 10분  
- 탐지까지 5분, 차단까지 4분  
- 전체 복구 완료까지 7시간  
- 복제된 저장소: 669개 / 영향받은 브랜치: 199개 / 폐쇄된 PR: 42개  

### 참고 리소스
- Socket.dev: *Shai-Hulud Strikes Again V2*  
- PostHog, Wiz, Endor Labs, HelixGuard의 분석 보고서  
- npm Trusted Publishers, pnpm `onlyBuiltDependencies`, `minimumReleaseAge`, Granted 문서

## Comments



### Comment 47790

- Author: click
- Created: 2025-12-15T21:33:33+09:00
- Points: 1

pnpm은 기본값으로 post-install을 개별적으로 허용해야하는 구조였을텐데 결국 개발자도 무의식적으로 허용하게 되나봅니다

### Comment 47824

- Author: lamanus
- Created: 2025-12-16T11:37:49+09:00
- Points: 1
- Parent comment: 47790
- Depth: 1

npm은 기본값으로 실행하게 되어있어서 pnpm으로 전환해서 기본 비활성 전환하여 보안을 강화한 것으로 이해됩니다.

### Comment 47782

- Author: neo
- Created: 2025-12-15T17:33:19+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=46262021) 
- npm install을 실행하는 건 **태만**이 아님  
  문제는 패키지 설치 과정에서 **임의 코드 실행을 허용하는 생태계**임  
  하지만 근본적인 보안 실패는, 제3자가 아무 검증 없이 내 제품에 코드를 밀어넣을 수 있는 패키지 매니저를 사용하는 것임  
  결국 우리는 패키지 관리자와 그 운영자들의 선의와 역량에 무한히 의존하고 있음  
  또 OP가 자격 증명을 평문으로 파일 시스템에 저장했다고 암시하는 것 같음  
  - 두 가지 모두 문제라고 생각함  
    언어 차원에서 코드가 입력을 읽고, 자원을 소비하고, **타입이 올바른 출력만 생성**하도록 제한하는 구조를 만들 수 있음  
    완전한 공급망 문제 해결은 아니지만, 노출 범위는 훨씬 줄어듦  
  - 이런 논리는 너무 순환적임  
    “한 사람이 이런 도구를 쓰는 건 잘못이 아니지만, 모두가 쓰면 생태계가 문제다”라는 식임  
    이미 많은 개발 도구가 **보안 취약**하다는 건 여러 번 증명된 사실임  
    진짜로 신경 쓴다면, 행동으로 보여야 함  
  - IDE 플러그인도 마찬가지임  
    VS Code를 쓸 때 작은 기능 하나 추가하려고도 누가 만든지도 모르는 플러그인을 설치해야 해서 불편했음  
  - 제3자 코드 실행을 막는 패키지 매니저를 어떻게 설계할 수 있을지 궁금함  
    결국 **신뢰할 수밖에 없는 코드**를 실행하게 되는 구조 아닌가 생각함  
  - 일부 도구는 http 인증을 위해 netrc 파일만 지원함  
    git을 http로 쓰면 이런 **평문 자격 증명 노출 경로**는 거의 항상 존재함  

- pnpm 메인테이너가 1년 전에 “**post-install 스크립트를 기본 차단**하자”고 제안했음  
  사용자 입장에서는 불편하겠지만, 장기적으로는 모두가 감사하게 될 변화라 믿었음  
  관련 PR: [pnpm/pnpm#8897](https://github.com/pnpm/pnpm/pull/8897)
  - 그런데도 여전히 같은 문제가 반복되고 있음  
    결국 **편의성이 보안을 이긴** 또 한 번의 사례임  

- “데이터베이스는 침해되지 않았다”고 했지만, 공격자가 **AWS와 시크릿에 접근**했다면 이미 침해된 것이라 봄  
  접근 가능성이 있었다면 침해로 간주해야 함  
  - AWS 리소스 접근 로그가 있고, 권한 회수 전 접근 흔적이 없다면 데이터는 안전하다고 볼 수 있음  

- 악성코드가 실행된 후에는 **출처 추적이 거의 불가능**함  
  pnpm install도 정상적으로 완료되니 탐지하기 어려움  
  Sentinel One이나 CrowdStrike 같은 **EDR**이 있었다면 조사 단서가 더 많았을 것임  
  - EDR이 있었다면 Trufflehog 공격 체인을 탐지하거나 차단했을 가능성이 높음  

- “총 669개의 repo를 클론했다”는 부분이 눈에 띔  
  직원 수가 100명도 안 되는 회사가 600개 넘는 repo를 가진 게 정상인지 궁금함  
  - 완전히 정상임. **repo는 가축이지 반려동물이 아님**  
  - 우리 조직은 7명인데 GitHub에 365개 repo가 있음  
    팀 규모보다 **연차와 프로젝트 수명**이 repo 수에 더 큰 영향을 줌  
  - 마이크로서비스를 좋아하는 아키텍트가 있으면 이런 결과가 나옴  

- pnpm은 이미 preinstall 같은 **lifecycle 스크립트 자동 실행을 중단**했는데, 구버전을 썼던 것 같음  
  [관련 PR](https://github.com/pnpm/pnpm/pull/8897) 참고  
  - 기사 마지막에 최신 메이저 버전으로 업데이트했다고 언급함  
  - 그게 pnpm을 쓰는 주된 이유라 생각했는데 혼란스러움  
  - 결국 **구버전 패키지 매니저로 의존성 업데이트**를 한 셈이라면, 이건 그들의 책임임  
  - 프로젝트 자체에 postinstall 스크립트가 있었을 수도 있음  
    pnpm은 의존성의 스크립트는 막지만, 프로젝트 레벨 스크립트는 여전히 실행함  

- 투명한 **사후 분석(post-mortem)** 공유에 감사함  
  이런 사례가 업계 전체에 중요함  
  공격 트래픽이 일반 개발 트래픽과 구분 가능했는지 궁금함  
  우리도 dev 환경에서 **egress 필터링**을 강화하려 하지만 npm install이 자주 깨져서 고민임  
  - GitHub 조직의 **IP 허용 목록 기능**을 쓰면 이런 공격 방어에 도움이 될 듯함  

- 개인 노트북의 git 보안을 고민 중임  
  현재는 SSH 키를 로컬에 두고 push함  
  관리자 권한도 있어서 위험함. 더 안전하게 만들 방법이 궁금함  
  - 1Password를 써서 SSH 키와 Git 서명을 관리하고, GitHub OAuth나 CLI 로그인으로 push/pull 하는 구성을 추천함  
    이렇게 하면 **서명 키와 접근 키를 분리**할 수 있고, 관리자 계정은 별도로 관리 가능함  
    관련 문서: [1Password SSH Agent](https://developer.1password.com/docs/ssh/agent/), [Git Commit Signing](https://developer.1password.com/docs/ssh/git-commit-signing/), [GitHub OAuth](https://github.com/hickford/git-credential-oauth), [GitHub CLI Login](https://cli.github.com/manual/gh_auth_login)  
  - 나는 SSH 개인키를 **TPM에 저장**해서 PKCS11로 SSH agent에서 사용함  
    키가 외부로 유출될 수 없다는 장점이 있지만, 악성코드가 내 머신에서 동작하면 여전히 위험함  
    [Linux 예시](https://wiki.gentoo.org/wiki/Trusted_Platform_Module/SSH), [macOS 예시](https://gist.github.com/arianvp/5f59f1783e3eaf1a2d4cd8e952bb4acf)  
  - 노트북이 **감염되면 방어 수단이 없음**  
    TPM이나 Yubikey로 조금 어렵게 만들 수는 있지만, 완전한 방어는 불가능함  
    관리자 작업은 별도 전용 머신에서 하는 게 안전함  
  - Yubikey에 GPG 키를 넣고 gpg-agent로 SSH 인증을 하는 방법도 있음  
    push나 commit 시 PIN 입력으로 잠금 해제함  
  - main 브랜치 직접 push를 막고, **MFA를 필수화**하면 공격자가 바로 배포 브랜치에 접근하기 어려움  

- Torvalds 이름의 커밋은 감염 후 흔히 보이는 **표식(signature)** 이었음  
  Microsoft의 [공식 분석 글](https://www.microsoft.com/en-us/security/blog/2025/12/09/shai-hulud-2-0-guidance-for-detecting-investigating-and-defending-against-the-supply-chain-attack/)에서도 언급됨  
  이 웜은 매우 시끄러웠고, 일부 공격자는 노출된 자격 증명으로 **비공개 repo를 공개하거나 readme를 수정**해 홍보용으로 악용했음  

- 공격자가 파괴 행위 없이 **조용히 정보만 유출**했다면, 이런 탐지가 가능했을지 의문임
