AI 보안 스캐너가 해킹 도구가 됐다 — LiteLLM 공급망 공격 3시간의 기록
며칠 전, 한 개발자가 Cursor MCP 플러그인을 테스트하고 있었어요. 별다른 작업도 아니었는데 갑자기 컴퓨터 팬이 미친 듯이 돌더니 RAM이 순식간에 바닥나버렸더라고요. 원인을 추적하다 발견한 건 AI보안 역사에 기록될 만한 공급망 공격이었어요.
문제의 패키지는 LiteLLM. 하루 약 340만 번 다운로드되는, AI 개발자라면 거의 다 쓰는 오픈소스 라이브러리가 악성 코드에 감염된 채 PyPI에 올라가 있었더라고요.
3시간 동안 무슨 일이 있었나
3월 24일, UTC 기준 타임라인을 따라가 보면요.
- 10:39 — 악성 v1.82.7이 PyPI에 등록됐어요.
- 10:52 — 곧이어 v1.82.8도 올라왔고요.
- 11:48 — FutureSearch의 Callum McMahon이 이상 징후를 감지했고요.
- 13:38 — PyPI에서 두 버전 모두 삭제되면서 사태가 마무리됐어요.
노출 시간은 약 3시간. 짧다고 느낄 수 있지만, 하루 340만 다운로드면 분당 2,300건이 넘거든요. 3시간이면 수십만 건의 다운로드가 일어났을 가능성이 있고요. 이 사건에는 CVE-2026-33634가 부여됐고, CVSS 점수는 9.4로 최상위 위험 등급이에요.
공격자 그룹 TeamPCP — ShellForce라는 이름도 쓰더라고요 — 는 20,000개 이상의 저장소와 500,000개 이상의 계정에 영향을 줬다고 주장하고 있어요. 과장일 수도 있지만, DSPy·MLflow·OpenHands·CrewAI 같은 하류 프로젝트 10개 이상이 영향 범위에 포함됐다는 건 확인된 사실이에요.
AI보안의 빈틈 — 보안 스캐너가 해킹 도구로
이번 공격에서 가장 소름 돋는 부분은 침투 경로였어요. LiteLLM을 직접 뚫은 게 아니라, LiteLLM이 CI/CD 파이프라인에서 사용하던 보안 스캐너 Trivy를 먼저 장악했거든요.
3월 19일, 공격자는 Trivy의 GitHub 저장소에서 77개 버전 태그 중 76개를 덮어썼어요. 보안 취약점을 점검하라고 돌리는 도구가 이미 오염된 상태였던 거예요. LiteLLM은 이 Trivy를 버전 고정 없이 CI/CD에서 가져다 쓰고 있었고요.
오염된 Trivy가 CI/CD 환경에서 실행되면서 PyPI 발행 토큰을 탈취했더라고요. 그 토큰으로 악성 코드가 심어진 LiteLLM v1.82.7과 v1.82.8을 PyPI에 올렸어요. 공식 패키지 저장소에 정식 버전처럼 등록된 거라, pip install litellm 한 줄이면 감염되는 상황이었어요.
보안 도구를 신뢰하고 파이프라인에 넣어뒀는데, 그 도구 자체가 뒷문이 되어버린 거예요. AI보안 관점에서 공급망 신뢰 모델의 근본적 취약점을 보여주는 사례라고 할 수 있어요.
뭘 훔쳐갔나
악성 버전이 설치되면 시스템에서 다음 정보들이 수집되기 시작했어요.
- SSH 개인키
- AWS·GCP·Azure 클라우드 자격증명
- Kubernetes 접근 토큰
- 데이터베이스 비밀번호
- 암호화폐 지갑 정보
수집된 데이터는 AES-256-CBC와 RSA 4096비트로 암호화돼서 models.litellm[.]cloud라는 도메인으로 전송됐더라고요. 공식 도메인처럼 보이지만, LiteLLM과 아무 관계 없는 공격자 소유 서버였어요.
v1.82.8에서는 한 단계 더 나갔어요. .pth 파일을 Python site-packages 디렉터리에 심었는데, 이 파일의 특성이 무서운 게 — Python 인터프리터가 시작될 때마다 자동으로 실행된다는 점이거든요. pip install이든 python script.py든, Python이 뜨기만 하면 멀웨어가 같이 돌아간 거죠.
이게 발견자 Callum McMahon이 겪은 RAM 폭주의 원인이었어요. .pth 파일이 모든 Python 프로세스에서 실행되면서 일종의 포크 폭탄이 터진 거였더라고요.
거기서 끝이 아니었어요. 멀웨어는 시스템 서비스로 자신을 등록해서 재부팅 후에도 살아남았고, Kubernetes 환경에서는 노드 간 횡이동까지 시도했더라고요. 한 대가 감염되면 클러스터 전체가 위험해지는 구조였어요.
| 구분 | v1.82.7 | v1.82.8 |
|---|---|---|
| 데이터 탈취 | SSH키, 클라우드 자격증명, DB 비밀번호 등 수집·전송 | 동일 + 범위 확대 |
| .pth 파일 삽입 | 없음 | 모든 Python 실행 시 자동 동작 |
| 지속성 확보 | 프로세스 종료 시 사라짐 | 시스템 서비스 등록, 재부팅 후에도 유지 |
| 횡이동 | 제한적 | K8s 노드 간 이동 시도 |
AI보안 — 공급망 공격이 남긴 교훈
이번 사건은 AI 개발 생태계의 구조적 약점을 그대로 드러냈어요. LiteLLM 자체에 보안 버그가 있었던 게 아니라, 의존성 체인의 한 고리가 무너지면서 연쇄적으로 전체가 무너졌어요.
Trivy처럼 널리 쓰이는 도구도 버전을 고정하지 않으면 공격 벡터가 될 수 있다는 걸 보여줬잖아요. AI 프로젝트들은 대체로 의존성이 깊고 넓어서, 한 지점의 침해가 수십만 개발 환경으로 퍼져나갈 수 있어요. AI보안은 자기 코드만 잘 짜는 것으로는 부족하다는 뜻이기도 하고요.
자주 묻는 질문
Q. 내가 영향받았는지 어떻게 확인하나요?
터미널에서 find / -name "litellm_init.pth" 2>/dev/null을 실행해 .pth 파일이 있는지 확인하세요. 그리고 ~/.config/sysmon/ 디렉터리가 존재하는지도 살펴봐야 해요. 이 경로에 멀웨어가 자신을 숨겨두는 거라서요. 두 가지 모두 발견되지 않으면 안전하다고 볼 수 있어요.
Q. Docker 환경에서 쓰고 있었으면 괜찮나요?
ghcr.io/berriai/litellm 공식 이미지를 사용하고 있었다면 안전해요. 이 이미지는 빌드 시점에 특정 버전으로 핀 고정돼 있어서 PyPI의 악성 버전이 끼어들 틈이 없었어요. 다만 Docker 안에서 직접 pip install litellm을 실행하는 커스텀 이미지를 쓰고 있었다면, 감염 가능성을 점검해 봐야 해요.
Q. 앞으로 이런 공격을 막으려면 어떻게 해야 하나요?
세 가지를 점검하면 돼요. 첫째, 모든 의존성 버전을 정확하게 핀 고정하세요. litellm==1.82.6처럼 특정 버전을 명시하는 거예요. 둘째, 패키지 설치 시 해시 검증을 활성화하세요. pip install --require-hashes 옵션을 사용하면 변조된 패키지를 걸러낼 수 있어요. 셋째, CI/CD 파이프라인의 PyPI 발행에 정적 토큰 대신 OIDC를 사용하면 토큰 탈취 자체를 막을 수 있어요.
pip install 한 줄. 개발자라면 하루에도 수십 번 치는 명령어잖아요. 그 한 줄 뒤에서 340만 건의 다운로드가 오가고, 보안 스캐너가 뒷문이 되고, SSH 키가 암호화돼서 어딘가로 날아가더라고요. 바이브코딩이 편하다고 보안까지 바이브로 할 순 없듯이, 의존성 하나하나가 내 인프라의 일부라는 감각을 잃지 않았으면 해요.
