Shai-Hulud 테마 악성코드가 PyTorch Lightning AI 학습 라이브러리에서 발견됨 | Semgrep
1 week ago
15
PyPI lightning 2.6.2 와 2.6.3 버전이 2026년 4월 30일 게시된 뒤 공급망 공격에 악용됐고, pip install lightning만으로 숨겨진 _runtime 디렉터리와 난독화된 JavaScript 페이로드가 실행될 수 있음
악성 페이로드는 모듈 임포트 시 자동 실행돼 자격 증명 , 인증 토큰, 환경 변수, 클라우드 비밀값을 탈취하며 GitHub 저장소 오염도 시도함
이번 공격은 진입점이 PyPI 이지만 웜 전파는 npm 을 통해 이뤄지며, npm 게시 자격 증명을 찾으면 게시 가능한 패키지에 setup.mjs 드로퍼와 router_runtime.js를 주입하고 패치 버전을 다시 게시함
데이터 유출은 HTTPS POST, GitHub 커밋 검색 데드드롭, 공격자 제어 공개 GitHub 저장소, 피해자 저장소 직접 푸시 등 4개 병렬 채널 을 사용하며 EveryBoiWeBuildIsAWormyBoi 커밋 접두사와 "A Mini Shai-Hulud has Appeared" 저장소 설명이 지표로 남음
악성코드는 Claude Code 의 .claude/settings.json SessionStart 훅과 VS Code 의 .vscode/tasks.json runOn: folderOpen 작업을 심어 저장소를 열 때마다 드로퍼를 실행하며, 영향 기간에 악성 패키지를 임포트한 머신은 완전 침해된 것으로 취급해야 함
영향받는 패키지와 확인 절차
lightning은 이미지 분류기 구축, LLM 파인튜닝, 확산 모델 실행, 시계열 예측기 개발 팀의 의존성 트리에 자주 포함되는 딥러닝 프레임워크임
영향받는 패키지
lightning 버전 2.6.2
lightning 버전 2.6.3
Semgrep 고객 확인 절차
PyPI에서 npm으로 확산되는 구조
mini Shai-Hulud가 npm을 직접 겨냥한 것과 달리 이번 공격의 진입점은 PyPI 임
악성 페이로드는 여전히 JavaScript이며, 웜 전파는 npm 을 통해 발생함
실행된 악성코드가 npm 게시 자격 증명을 찾으면 해당 토큰으로 게시 가능한 모든 패키지에 setup.mjs 드로퍼와 router_runtime.js를 주입함
이후 scripts.preinstall을 드로퍼 실행으로 설정하고, 패치 버전을 올린 뒤 다시 게시함
해당 패키지를 설치한 하위 개발자는 전체 악성코드를 자신의 머신에서 실행하게 되며, 토큰 탈취와 패키지 웜 감염이 이어짐
데이터 유출 방식
탈취 기능은 이전 캠페인의 Mini Shai-Hulud 메커니즘과 설계를 공유하며, 개별 경로가 차단돼도 데이터가 빠져나가도록 4개의 병렬 채널을 사용함
공격에는 EveryBoiWeBuildIsaWormBoi라는 공개 저장소 생성 등 Shai-Hulud 테마가 포함됨
공격 지표 구조는 이전 mini Shai-Hulud 캠페인과 일치하며, 악성 커밋 메시지는 EveryBoiWeBuildIsAWormyBoi 접두사를 사용해 원래 Mini Shai-Hulud 공격과 구분됨
HTTPS POST를 통한 C2 전송
탈취 데이터는 포트 443을 통해 공격자 제어 서버로 즉시 POST됨
도메인과 경로는 페이로드 안에 암호화된 문자열로 저장돼 정적 분석을 어렵게 만듦
GitHub 커밋 검색 데드드롭
악성코드는 GitHub 커밋 검색 API를 폴링해 EveryBoiWeBuildIsAWormyBoi 접두사가 붙은 커밋 메시지를 찾음
커밋 메시지는 EveryBoiWeBuildIsAWormyBoi:<base64(base64(token))> 형식의 이중 Base64 인코딩 토큰을 운반함
디코딩된 토큰은 이후 작업을 위한 Octokit 클라이언트 인증에 사용됨
공격자 제어 공개 GitHub 저장소
무작위로 선택된 Dune 단어 이름과 "A Mini Shai-Hulud has Appeared" 설명을 가진 새 공개 저장소가 생성됨
이 설명은 GitHub에서 직접 검색 가능함
탈취 자격 증명은 results/results-<timestamp>-<n>.json으로 커밋되며, API를 통해 Base64 인코딩되지만 내부는 일반 JSON임
30MB를 넘는 파일은 번호가 붙은 청크로 나뉨
커밋 메시지는 위장용으로 chore: update dependencies를 사용함
피해자 저장소로 직접 푸시
악성코드가 ghs_ GitHub 서버 토큰을 얻으면 피해자의 GITHUB_REPOSITORY 모든 브랜치에 탈취 데이터를 직접 푸시함
탈취 대상
악성코드는 로컬 파일, 환경, CI/CD 파이프라인, 클라우드 제공자 전반의 자격 증명을 겨냥함
파일 시스템
80개 이상의 자격 증명 파일 경로를 스캔해 ghp_, gho_, npm_ 토큰을 찾음
파일당 최대 5MB까지 처리함
셸과 환경 변수
gh auth token을 실행함
process.env의 모든 환경 변수를 덤프함
GitHub Actions
Linux 러너에서 내장 Python으로 Runner.Worker 프로세스 메모리를 덤프함
"isSecret":true로 표시된 모든 비밀값과 GITHUB_REPOSITORY, GITHUB_WORKFLOW를 추출함
GitHub 조직
토큰 범위인 repo, workflow를 확인함
GitHub Actions 조직 비밀값을 순회함
AWS
환경 변수, ~/.aws/credentials 프로필, IMDSv2 169.254.169.254, ECS 169.254.170.2를 시도해 sts:GetCallerIdentity를 호출함
Secrets Manager의 모든 값과 SSM 파라미터를 열거하고 가져옴
Azure
DefaultAzureCredential을 사용해 구독을 열거하고 Key Vault 비밀값에 접근함
GCP
GoogleAuth로 인증함
Secret Manager의 모든 비밀값을 열거하고 가져옴
대상 범위는 로컬 개발 환경, CI 러너, 3대 주요 클라우드 제공자를 모두 포함함
영향을 받은 기간에 악성 패키지를 임포트한 모든 머신은 완전 침해 된 것으로 취급해야 함
개발 도구를 통한 지속성 확보
저장소 내부에 들어온 뒤 악성코드는 흔히 사용되는 개발 도구인 Claude Code 와 VS Code 를 겨냥해 지속성 훅을 심음
Claude Code
악성코드는 저장소의 Claude Code 설정 파일인 .claude/settings.json에 matcher: "*"인 SessionStart 훅을 작성함
훅은 node .vscode/setup.mjs를 가리킴
개발자가 감염된 저장소에서 Claude Code를 열 때마다 실행되며, 세션 시작 외의 도구 사용이나 사용자 동작은 필요하지 않음
VS Code
VS Code 사용자를 겨냥해 .vscode/tasks.json에 runOn: folderOpen 작업을 심음
프로젝트 폴더가 열릴 때마다 node .claude/setup.mjs가 실행됨
드로퍼 setup.mjs
두 훅은 모두 자체 포함형 Bun 런타임 부트스트래퍼인 setup.mjs를 호출함
Bun이 설치돼 있지 않으면 GitHub releases에서 bun-v1.3.13을 조용히 다운로드함
Linux x64, Linux arm64, Linux musl, macOS x64, macOS arm64, Windows x64, Windows arm64를 처리함
이후 전체 14.8MB 페이로드인 .claude/router_runtime.js를 실행하고 /tmp에서 정리함
악성 GitHub Actions 워크플로
악성코드가 쓰기 권한이 있는 GitHub 토큰을 보유하면 피해자 저장소에 Formatter라는 워크플로를 푸시함
모든 push에서 ${{ toJSON(secrets) }}를 통해 모든 저장소 비밀값을 덤프함
결과를 format-results라는 다운로드 가능한 Actions 아티팩트로 업로드함
Actions는 정상처럼 보이도록 특정 커밋 SHA에 고정됨
CI에서 감염된 lightning 패키지를 받았고 쓰기 권한 토큰을 가진 저장소는 해당 파일들을 감사해야 함
침해 지표
검색 가능한 지표
EveryBoiWeBuildIsAWormyBoi 접두사가 붙은 커밋 메시지는 데드드롭 토큰 운반자로 사용되며 GitHub 커밋 검색으로 찾을 수 있음
"A Mini Shai-Hulud has Appeared" 설명을 가진 GitHub 저장소는 공격자 유출 저장소이며 직접 검색 가능함
패키지
lightning@2.6.2
lightning@2.6.3
파일과 시스템 아티팩트
_runtime/start.py: 임포트 시 페이로드를 초기화하는 Python 로더
_runtime/router_runtime.js: 난독화된 JavaScript 페이로드이며 14.8MB Bun 런타임임
_runtime/: 악성 패키지 버전에 추가된 디렉터리
.claude/router_runtime.js: 피해자 저장소에 주입된 악성코드 복사본
.claude/settings.json: 피해자 저장소에 주입된 Claude Code 훅 설정
.claude/setup.mjs: 피해자 저장소에 주입된 드로퍼
.vscode/tasks.json: 피해자 저장소에 주입된 VS Code 자동 실행 작업
.vscode/setup.mjs: 피해자 저장소에 주입된 드로퍼
Homepage
Tech blog
Shai-Hulud 테마 악성코드가 PyTorch Lightning AI 학습 라이브러리에서 발견됨 | Semgrep
🔉 볼륨 줄이기
🔊 볼륨 키우기
🔇 음소거
⏭️ 다음 곡