본문으로 건너뛰기

© 2026 Molayo

HN분석2026. 05. 14. 04:22

사후 분석: TanStack NPM 공급망 침해

요약

2026년 5월 11일, 공격자가 @tanstack/* npm 패키지 42개에 걸쳐 84개의 악성 버전을 게시하는 대규모 공급망 침해 사건이 발생했습니다. 이 공격은 PR 포크 코드 실행, 기본 저장소 캐시 접근, 그리고 릴리스 워크플로우의 `id-token: write` 권한을 연결하는 세 가지 취약점을 체인으로 활용하여 성공했습니다. 탐지는 StepSecurity 소속 외부 연구원 ashishkurmi가 침해 발생 후 약 20분 만에 상세 분석 이슈를 제기하면서 이루어졌으며, 이는 공급망 보안의 중요성을 다시 한번 강조합니다.

핵심 포인트

  • 공격자는 PR 포크 코드 실행, 기본 저장소 캐시 접근, 릴리스 워크플로우 권한을 연결하는 세 가지 취약점을 체인으로 활용했습니다.
  • 악성 코드는 `optionalDependencies`를 통해 공격자가 제어하는 바이너리를 빌드 단계에서 호출하도록 설계되었습니다.
  • 이 침해는 외부 연구원(StepSecurity의 ashishkurmi)에 의한 신속하고 전문적인 분석 및 보고 덕분에 탐지될 수 있었습니다.
  • 공격자는 새로운 기술을 개발하기보다, 기존에 발표된 메모리 추출 기법 등을 재조합하여 사용했습니다.

Tanner Linsley가 2026년 5월 11일에 작성.

최종 업데이트: 2026-05-11

2026-05-11 UTC 기준 19:20부터 19:26 사이에 공격자가 다음을 결합하여 42개의 @tanstack/* npm 패키지에 걸쳐 84개의 악성 버전을 게시했습니다. 즉, pull_request_target

시간이벤트
2026-05-10 17:16공격자가 github.com/zblgg/configuration을 포크함 (TanStack/router의 포크본이며, 포크 목록 검색을 회피하기 위해 의도적으로 이름이 변경됨)
...
시간이벤트
------
2026-05-11 19:15Manuel이 PR #7369를 병합함 (Shkumbin의 CSS.supports 수정) → main에 푸시되면서 release.yml 워크플로우가 실행됨 25613093674가 시작되고(19:15:44), 실패함.
...
시간이벤트
------
2026-05-11 ~19:50StepSecurity 소속 외부 연구원 ashishkurmi가 악성 optionalDependencies의 지문(fingerprint)과 패키지 목록(초기에는 42개 중 14개)을 담은 완전한 분석 글이 포함된 이슈 #7383를 열었음
...
세 가지 취약점이 연결되어 있음. 각 취약점은 공격에 필수적이며, 단독으로는 충분하지 않음.

bundle-size.yml은 포크 PRs에 대해 pull_request_target을 실행했고, 그 트리거 컨텍스트 내에서 포크의 PR-merge ref를 체크아웃하여 빌드를 실행함:

on:
pull_request_target:
paths: ['packages/**', 'benchmarks/**']
...
on:
pull_request_target:
paths: ['packages/**', 'benchmarks/**']
...

워크플로우의 작성자는 신뢰 분리(trust split)를 시도했음 (comment-pr 작업은 benchmark-pr과 별개이며, YAML에 주석을 달아 benchmark-pr을

release.yml에는 id-token: write가 선언되어 있습니다 (npm OIDC 신뢰 출판을 위해 합법적으로 필요함). 오염된 pnpm 스토어가 러너(runner)에서 복원될 때, 공격자가 제어하는 바이너리가 디스크에 존재하게 되고 빌드 단계 중에 호출됩니다. 이 바이너리들은 다음과 같습니다:

이것은 2025년 3월 tj-actions/changed-files 침해에서 사용된 것과 동일한 메모리 추출 기술(그리고 출처 표기 주석이 포함된 문자 그대로의 Python 스크립트)입니다. 공격자는 새로운 트레이드크래프트(tradecraft)를 발명하지 않았습니다. 그들은 발표된 연구들을 재조합했을 뿐입니다.

이 체인(chain)은 각 취약점이 다른 것들이 가정했던 신뢰 경계(trust boundary)를 연결하기 때문에 작동합니다: PR 포크 코드 $
ightarrow$ 기본 저장소 캐시, 기본 저장소 캐시 $
ightarrow$ 릴리스 워크플로우 런타임, 그리고 릴리스 워크플로우 런타임 $
ightarrow$ npm 레지스트리 쓰기 접근 권한.

탐지는 외부적이었습니다. StepSecurity에서 근무하는 외부 연구원 ashishkurmi가 출판 후 약 20분 만에 전체 기술 분석과 함께 이슈 #7383을 열었습니다. Tanner는 워룸(war room)을 시작한 직후 Socket.dev로부터 전화를 받아 상황을 확인했습니다.

모든 @tanstack/* 패키지의 매니페스트에서:

"optionalDependencies": {"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}
"optionalDependencies": {"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}

이것들은 우리가 사후 분석(postmortem)을 닫기 전에 답변이 필요합니다.

영향받은 버전의 전체 목록은 GitHub Security Advisory (GHSA-g7cv-rxg3-hmpx)를 참조하십시오.

AI 자동 생성 콘텐츠

본 콘텐츠는 HN AI Posts의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0