[tool.maturin]
data = "cook"
2026년 10월 21일 11시 39분 51초(UTC), 오픈소스 대형 언어 모델(LLM) 게이트웨이 LiteLLM이 1.107.0 정식 버전을 PyPI에 배포했습니다. 소스 코드의 pyproject.toml에 추가된 것은 오직 이 한 줄의 설정뿐이었습니다. 그러나 그 후 2시간 동안, 버전을 고정하지 않은 모든 배포 노드는 공격자의 자격 증명 수집기로 전락했습니다. AI 인프라의 신뢰 경계는 ‘의존성 소스 코드 감사’에서 ‘빌드 자격 증명 사수’로 무너져 내렸습니다. 그리고 배포의 마스터키를 공격자의 손에 쥐여준 것은 다름 아닌 개발자가 일상적으로 사용하던 AI 코딩 어시스턴트였습니다.
12바이트 훅이 인터프리터를 장악하다, 임포트하지 않아도 실행
litellm==1.107.0은 PyPI에서 13시 23분(UTC)까지 약 2시간 동안 노출되었습니다. 리눅스, macOS, 윈도우용 7개 wheel 패키지 전체에 악성 페이로드가 포함되었습니다. 동시에 Docker Hub와 GHCR에 등록된 v1.107.0, latest, main-latest, main-stable 태그 4개 역시 변조되었습니다. 11시 46분 56초부터 14시 20분(UTC)까지 이 태그들은 일제히 오염된 이미지 sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e를 가리켰습니다. 버전을 고정하지 않은 CI/CD 파이프라인이 업데이트를 가져오는 즉시 운영 환경에 백도어가 직접 설치되었습니다.
악성 코드는 Maturin의 data = "cook" 설정을 통해 wheel 패키지의 purelib/ 디렉터리에 침투했습니다. 패키지 설치 시 site-packages 디렉터리에 3개 파일이 생성됩니다. 12바이트 크기의 cook.pth, 22,252바이트의 cook.py, 그리고 25,509바이트의 sitecustomize.py입니다. 공식 저장소인 BerriAI/litellm에는 cook/ 디렉터리가 존재한 적이 없으며 소스 배포 파일에도 관련 코드는 없었습니다. 깃허브 공개 커밋 내역만 검토하는 기존 보안 감사는 빌드 시점의 코드 주입 공격 앞에서 완전히 무력했습니다.
cook.pth의 내용은 import cook 단 한 줄에 불과합니다. 파이썬의 .pth 메커니즘에 따라 인터프리터가 시작될 때 import로 시작하는 구문이 자동으로 실행됩니다. 동일 디렉터리의 sitecustomize.py 역시 시동 시 자동 로드되는 글로벌 훅으로, cook.py의 압축 페이로드와 함께 이중 실행 구조를 형성했습니다. 공격 페이로드는 애플리케이션의 정상 임포트 경로를 완전히 건너뜁니다. 호스트 머신에서 import litellm을 단 한 번도 실행하지 않더라도 pip 명령을 내리거나 IDE의 언어 서버를 시작하는 것만으로 백도어가 즉시 트리거됩니다.
그림: LiteLLM 1.107.0 악성 패키지의 파일 주입 및 2단계 페이로드 실행 흐름. 출처: SafeDep(공급망 보안 분석 보고서)
AI 개발 도구와 게이트웨이 DB를 집중 겨냥한 16개 탈취 모듈
1단계 페이로드인 cook.py에는 16개의 자격 증명 수집 모듈이 내장되어 대상 머신 전체를 샅샅이 뒤졌습니다. 먼저 ~/.ssh/* 개인키와 셸 히스토리를 읽고, Chromium과 Firefox에 저장된 비밀번호를 DPAPI나 마스터키로 복호화했습니다. 이어 AWS, Azure, GCP, ~/.kube/config, Docker, gh, Vault, Terraform의 설정 파일과 인증 토큰을 수집했습니다. 나아가 AWS IMDS 메타데이터 엔드포인트를 호출하고(IMDSv2를 시도한 뒤 IMDSv1으로 폴백) 실행 중인 프로세스의 /proc/<pid>/environ을 순회하며 환경 변수를 가로챘습니다. 현대 클라우드 네이티브 엔지니어의 작업 환경에 맞춰 정밀하게 설계된 페이로드였습니다.
공격자의 의도를 가장 극명하게 보여준 것은 ai_devtools와 db_litellm이라는 두 개의 전용 모듈이었습니다. ai_devtools는 Claude Code, Claude Desktop, Cursor, Windsurf, Copilot, VS Code의 mcp.json, Gemini CLI, Codex, Continue, Aider, Zed, Hugging Face 등 17종의 AI 개발 도구 자격 증명을 집중 수집했습니다. db_litellm은 DATABASE_URL을 파싱하여 게이트웨이 데이터베이스의 LiteLLM_CredentialsTable과 LiteLLM_ProxyModelTable을 통째로 덤프했습니다. AI 게이트웨이는 기업 전체 LLM 호출의 인증 허브 역할을 합니다. 단 한 대의 LiteLLM 프록시 인스턴스가 뚫리면 그 뒤에 연동된 모든 업스트림 모델의 API 키가 한꺼번에 유출되는 셈입니다.
탈취된 정보는 무작위 AES-256-CBC 키로 암호화된 후 내장된 RSA-4096 공개키로 OAEP 캡슐화되었습니다. 압축된 tar.gz 아카이브는 X-Token: XWiA8gZNa7NcX2XRoAEb 헤더와 함께 www.litellm.dev/healthcheck로 전송되었습니다. 2단계 페이로드인 sitecustomize.py는 /tmp/remote_code_loader.lock 파일을 잠그고 fork()와 setsid()를 통해 상주 데몬으로 전환되었습니다. 3,600초마다 깃허브 letters9813/pythonclient-test 저장소의 domain.txt를 확인하여 정보 수집을 반복하고, 원격 /codepy 스크립트를 다운로드하여 exec()로 직접 실행했습니다. 깃허브는 13시 40분(UTC) 무렵 letters9813 계정을 차단했으나, 그전에 네트워크에 연결되어 있던 시스템은 이미 2단계 코드를 실행했을 가능성이 큽니다. 여기에 진짜 도메인과 매우 유사한 litellm.dev를 조합하여 일반적인 네트워크 트래픽 모니터링으로는 2시간 안에 이상 징후를 감지하기 어려웠습니다.
세 개의 특권 키가 승인 장벽을 통과, 강화된 파이프라인이 악성 빌드를 직접 서명
올해 3월 보안 사고를 겪은 후, LiteLLM 팀은 릴리스 파이프라인의 보안을 대폭 강화한 상태였습니다. 빌드 워크플로를 비공개 저장소로 이전하고, Trusted GitHub Publishers를 활성화했으며, 불변 Docker 태그를 적용하고 컨테이너 이미지 서명을 의무화했습니다. 원칙적으로 외부 침입자가 릴리스 스크립트를 변조하거나 공식 서명을 위조하는 것은 불가능한 구조였습니다.
그러나 10월 21일의 로그는 정당한 열쇠를 손에 쥔 침입자가 시스템을 무력화한 과정을 보여줍니다. 10시 08분 29초(UTC), 브랜치 보호 규칙이 PR 리뷰를 거치지 않은 워크플로 수정을 정상적으로 차단했습니다. 그러나 불과 5초 뒤인 10시 08분 34초, 공격자는 조직 소유자 권한의 GitHub CLI 토큰을 이용해 브랜치 보호를 강제로 해제하고 커밋 9cf97e935aeaf791b9c8db224a2bb3ee156134ce를 밀어 넣었습니다. 10시 46분 39초, 변조된 워크플로는 이름이 유사한 타이포스쿼팅 저장소 Berri-AI/litellm(정식 명칭 BerriAI와 하이픈 하나 차이)의 커밋 20535d8fa를 가져와 사전 배포 빌드를 통과시켰습니다. 11시 11분 18초, 두 번째 개발자 자격 증명으로 프로덕션 배포 승인 관문이 통과되었습니다. 11시 53분 악성 패키지 배포가 끝난 뒤, 세 번째 자격 증명으로 운영 및 사전 배포의 실행 기록이 삭제되었습니다. 공격자가 실제 고권한 자격 증명 세 개를 쥐고 있을 때, 자동 서명과 승인 관문은 공격을 막지 못할 뿐만 아니라 악성 빌드에 공식 인증 도장을 찍어주는 수단으로 전락했습니다.
그림: LiteLLM 공식 보안 권고문에서 공개한 10월 21일 파이프라인 침해 사고 타임라인. 출처: LiteLLM(공식 보안 공지)
Claude Code가 응답에 토큰을 포함, 공개 로그가 권한 상승의 발판으로 작동
핵심 관리자의 깃허브 자격 증명은 어떻게 유출되었을까요? LiteLLM 팀의 공식 사후 분석을 통해 결정적인 경위가 드러났습니다. 특정 조건에서 Claude Code가 사용자의 GitHub CLI OAuth 토큰을 API 응답 본문에 그대로 포함해 반환했던 것입니다. 게이트웨이에서 응답 본문 로깅이 활성화되어 있다면, 이 고권한 토큰들은 LiteLLM의 로그 파일에 평문으로 남게 됩니다. 팀은 이후 내부 네트워크에서 인터넷에 직접 노출되어 있던 무명의 LiteLLM 인스턴스를 발견했습니다. 공격자는 최근 패치된 취약점을 통해 해당 인스턴스에 침투한 뒤 로그 파일에서 GitHub CLI 토큰을 회수한 것으로 확인되었습니다.
이 복합적인 유출 사고를 두고 보안 커뮤니티에서는 두 가지 분석 시각이 맞섰습니다. AI 어시스턴트를 비판하는 측은 로컬 에이전트가 호스트 머신에 대해 지나치게 넓은 접근 권한을 쥐고 있으며, gh 토큰을 응답 스트림에 포함하는 것은 최소 권한 원칙과 데이터 마스킹 원칙을 심각하게 위반한 것이라고 지적했습니다. 반면 인프라 운영을 지적하는 측은 클라이언트가 무엇을 전송하든 응답 본문 전체를 기록하는 게이트웨이를 공용 인터넷에 노출한 것 자체가 중대한 구성 실수라고 짚었습니다. 두 시각은 동일한 엔지니어링 현실을 가리킵니다. 개발자가 AI 어시스턴트로 게이트웨이 코드를 작성하는 동안, 어시스턴트는 게이트웨이 로그에 마스터키를 쏟아내고 있었습니다. AI 도구 체인의 닫힌 순환 구조가 단일 실패 지점의 파급력을 폭발적으로 키운 것입니다.
복구의 첫 단계는 pip 실행 금지, 버전 고정만이 최선의 방어선
1.107.0 버전을 설치한 환경을 위해 공식 복구 가이드는 직관에 반하는 원칙을 최우선으로 제시합니다. 복구를 위해 절대로 pip install 명령을 먼저 실행하지 마십시오. pip 자체도 파이썬 프로세스이므로 실행 시 cook.pth와 sitecustomize.py가 다시 기동되기 때문입니다. 올바른 복구 절차는 site-packages 디렉터리 아래의 cook.pth, cook.py, sitecustomize.py 파일과 __pycache__ 캐시를 수동으로 먼저 삭제한 뒤, /tmp/remote_code_loader.lock 파일을 점유하고 있는 데몬 프로세스를 강제 종료하는 것입니다. 정리가 완료된 후에야 1.106.2로 롤백하거나 컨테이너 및 가상환경을 처음부터 다시 빌드해야 합니다.
악성 파일을 삭제하는 것은 응급조치일 뿐입니다. 진정한 핵심은 모든 자격 증명의 전면 교체입니다. 공식 권고는 경고합니다. “1.107.0이 외부 인터넷 통신이 가능한 호스트에 한 번이라도 설치되었다면, 이미 정보 유출이 완료되었다고 가정해야 합니다.” 컨테이너 재부팅이나 /tmp 정리는 락 파일을 지워버리므로, 락 파일이 보이지 않는다고 해서 안전한 것은 아닙니다. 호스트 머신의 IAM 역할, 프로세스 환경 변수, DATABASE_URL 비밀번호, 프록시 DB에 저장된 각 모델 공급자 API 키를 빠짐없이 교체해야 하며, 설치 시점을 전후하여 AWS CloudTrail에서 발생한 GetCallerIdentity 호출 내역을 면밀히 조사해야 합니다. 한편 1.106.2, 1.107.0rc1, 1.107.0rc2 및 버전이 고정된 litellm-proxy-extras==0.4.110은 안전한 것으로 확인되었습니다. 오직 버전을 고정하지 않은 사용자들만이 이번 2시간 번개 작전의 피해자가 되었습니다.
소프트웨어 공급망 공격의 전선이 완전히 이동했습니다. 공격자는 더 이상 깃허브 저장소에 은밀한 PR을 올리는 데 시간을 쓰지 않습니다. 인터넷에 노출된 로그 파일에서 토큰을 건져 올리고, 공식 빌드 파이프라인을 통해 악성 패키지를 공인 서명하여 배포합니다. AI 코딩 어시스턴트가 매일 개발 환경의 민감한 자격 증명을 다루는 오늘날, 게이트웨이 로그를 극비 기밀로 철저히 격리하고 프로덕션의 모든 의존성을 해시값으로 잠그는 것만이 AI 인프라의 신뢰 경계를 지키는 길입니다.
참고 링크:
- SafeDep 공급망 보안 분석 보고서: LiteLLM PyPI 침해 및 자격 증명 탈취 백도어 심층 분석
- LiteLLM 공식 보안 공지: 2026년 10월 21일 보안 사고 사후 분석
- Hacker News 커뮤니티의 LiteLLM 1.107.0 공급망 침해 사건 토론