Python .pth 파일을 이용한 서플라이 체인 공격 — LiteLLM 사례 분석
pip install만으로 전체 시스템이 탈취되는 공격 체인의 구조
LiteLLM은 OpenAI, Anthropic, Google 등 여러 LLM 프로바이더를 통합하는 Python 라이브러리다. 월 9500만 다운로드. 이 라이브러리가 2026년 3월에 서플라이 체인 공격을 당했다.
서플라이 체인 공격이 뭔가
소프트웨어를 직접 공격하는 게 아니라, 그 소프트웨어가 의존하는 라이브러리나 빌드 도구를 공격하는 방식이다. npm, PyPI, RubyGems 같은 패키지 저장소에 악성 코드를 심어놓으면, 그걸 설치하는 모든 사용자가 감염된다.
직접 공격은 방화벽·인증으로 막을 수 있다. 서플라이 체인 공격은 "정상적인 패키지 업데이트"로 위장하기 때문에 기존 보안 체계를 우회한다. 개발자가 매일 하는 pip install이 공격 벡터가 된다.
이번 공격 경로
- 먼저 Trivy(취약점 스캐너) v0.69.4~v0.69.6이 공격당했다
- Trivy의 CI/CD 파이프라인에서 PyPI 크레덴셜이 유출되었다
- 유출된 크레덴셜로 LiteLLM 메인테이너 계정을 탈취
- 악성 코드가 포함된 v1.82.7, v1.82.8을 PyPI에 직접 업로드
이 공격의 무서운 점은 import litellm을 안 해도 설치만 하면 코드가 실행된다는 것이다.
.pth 파일이 뭔가
Python의 site-packages/ 디렉토리에 .pth 파일이 있으면 Python 인터프리터가 시작할 때 자동으로 읽는다. 원래는 패키지 경로를 추가하는 용도인데, 줄이 import로 시작하면 그 줄을 그대로 실행한다.
pip install litellm → wheel 파일 전개 → site-packages에 .pth 파일 설치 → Python 시작 시 자동 실행.
4단계 페이로드
Stage 0 — .pth 파일이 subprocess를 생성
Stage 1 — RSA-4096 공개키 + AES-256-CBC 암호화 루틴 로드
Stage 2 — 25곳 이상에서 크레덴셜 수집: SSH 키, AWS/GCP/Azure 토큰, K8s 서비스 어카운트 토큰, 11종 암호화폐 지갑, 셸 히스토리
Stage 3 — C2 서버에 50분 간격으로 폴링, 임의 바이너리 실행 가능
Kubernetes 클러스터 장악
tolerations에 operator: Exists + privileged: true로 전 노드에 특권 Pod를 생성. 호스트 파일시스템을 /host로 마운트해서 영구 백도어 설치.
영향 범위
19,262개 의존 레포지토리 — OpenHands, DSPy(Stanford NLP), Databricks AI 포함.
실제 대응 — Trivy 사용자로서
필자는 CI/CD에서 trivy-action을 쓰고 있었다. 탈취가 발생한 3/19~3/20에 실제로 워크플로우가 실행되기도 했다.
다행히 trivy-action을 버전 태그가 아닌 full-length commit SHA로 고정해두고 있었다. v0.28.0 시점의 SHA를 직접 지정해서 쓰고 있었기 때문에, 탈취된 v0.69.4~v0.69.6으로 자동 업데이트되지 않았다.
확인 절차로 3/19~3/20 기간의 GitHub Actions 로그를 전부 다운로드받아서, 탈취된 버전이 실행되지 않은 걸 검증했다. 그리고 만약 탈취되었더라도 해당 워크플로우의 env / secrets에 위험한 키(AWS 크레덴셜, PyPI 토큰 등)가 설정되어 있지 않았으므로 피해는 없었을 것이다.
GitHub Actions를 쓴다면 꼭 해둘 것
Repository Settings → Actions → General → Actions permissions → "Require actions to be pinned to a full-length commit SHA"를 체크해두면, SHA로 고정하지 않은 action을 사용할 때 워크플로우가 에러로 실행 거부된다.
# ❌ 태그 지정 — 태그가 탈취되면 악성 코드 실행
uses: aquasecurity/trivy-action@v0.28.0
# ✅ SHA 고정 — 특정 커밋의 코드만 실행
uses: aquasecurity/trivy-action@a23fa65c5ff3e8d0c5f578c4b2a2b69e3b03be3b
태그는 누구든 덮어쓸 수 있다. SHA는 못 바꾼다. 이 차이가 이번 사건에서 갈렸다.
기존 워크플로우의 action을 일괄로 SHA 고정하려면 pinact라는 OSS가 편하다. pinact run으로 전체 .github/workflows/ 파일의 태그를 SHA로 자동 변환해준다.
동작 흐름
Trivy(취약점 스캐너) 공격 → CI/CD에서 PyPI 크레덴셜 유출
유출된 크레덴셜로 LiteLLM 메인테이너 계정 탈취
.pth 파일 포함 악성 버전을 PyPI에 업로드
pip install만으로 .pth가 site-packages에 설치 → Python 시작 시 자동 실행
4단계 페이로드: subprocess → 암호화 → 크레덴셜 수집(25곳+) → C2 폴링