
دروبال 11.4.8 - عالي - عن بُعد - SSRF - اكتشاف oEmbed يقوم بطلبات GET لعنوان URL ثانٍ غير محدد النطاق
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · drupal-oembed-ssrf
المقال: https://abraxaslabs.tech/research/drupal-oembed-ssrf
الفئة: SSRF الوصول: عن بُعد
Drupal core 11.4.8 (61044508) - Drupal
قيّد SA-CORE-2026-008 مضيف مستند الاكتشاف باستخدام media_oembed_discovery_trusted_host_patterns. لا يزال UrlResolver::findUrl يُرجع href الخاص بـ link كما هو. بعد ذلك يقوم ResourceFetcher::fetchResource بتنفيذ GET على ذلك العنوان دون أي فحص للمضيف أو لعناوين IP الخاصة. وthumbnail_url في oEmbed من نفس فئة طلب GET. الأنماط الافتراضية فارغة، لذا يكون الاكتشاف معطّلًا حتى يقوم المسؤول بتفعيله.
بعد أن يفعّل المسؤول الاكتشاف، يمكن لمستخدم يملك صلاحية إنشاء وسائط oEmbed أن يوجّه Drupal إلى صفحة مدرجة في قائمة السماح يحتوي رابط oEmbed JSON فيها على href يشير إلى loopback أو RFC1918 أو metadata.
| المعرّف | لا يوجد CVE بعد |
| الفئة | SSRF (بقايا القفزة الثانية من SA-CORE-2026-008) |
| الوصول | عن بُعد (إنشاء وسائط موثّق، بعد أن يضبط المسؤول مضيفي الاكتشاف) |
| CWE | CWE-918 |
| CVSS | متوسط: 6.1 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N |
| المنتج | Drupal core |
| المتأثر | 11.4.8 (61044508071749629aaf3380aa85971e94df9e84) |
| المصادقة | إنشاء وسائط موثّق، بعد أن يضبط المسؤول مضيفي الاكتشاف. AC:H هو ذلك المفتاح الذي يفعّله المسؤول. |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
فقط بعد أن يضبط المسؤول أنماط المضيفين الموثوقين للاكتشاف. بعد ذلك يكفي منشئ وسائط. لا يمكن لشخص غريب تمامًا بلا حساب أن يفعل ذلك بالإعداد الافتراضي. هذا SSRF، وليس RCE ولا سرقة جلسة مسؤول.
| الصلاحية | الطريقة / نقطة الوصول | النوع | مُثبت مختبريًا أم مُستنتج |
|---|---|---|---|
| جعل Drupal ينفّذ GET على عنوان يختاره المهاجم (loopback في هذا المختبر) | UrlResolver::findUrl يُرجع href الخام ثم GET عبر ResourceFetcher::fetchResource | استطلاع (جلب من جهة الخادم) | مُثبت مختبريًا. أعاد http://127.0.0.1:18080/oembed القيمة DRUPAL-OEMBED-SSRF-WITNESS |
| قراءة جسم HTTP لذلك العنوان داخل PHP | نفس طلب GET؛ يُحلَّل JSON/XML كـ oEmbed | استطلاع | مُثبت مختبريًا. كان الشاهد في جسم الاستجابة 200 |
نفس طلب GET لـ thumbnail_url بعد بناء المورد | OEmbed::getLocalThumbnailUri ~417-456 | استطلاع | مُستنتج من الإصدار المثبّت. توقف هذا المختبر عند الـ href المكتشف |
| الوصول إلى RFC1918 / link-local / metadata إذا كانت شبكة PHP قادرة على ذلك | طلب GET عبر Guzzle بلا قيود | استطلاع | مُستنتج. استخدم المختبر loopback، وليس 169.254.169.254 |
مغلق في هذه البقايا: القفزة الأولى عندما تكون الأنماط فارغة (مُثبت مختبريًا)؛ القفزة الأولى عندما لا يطابق مضيف HTML (مُثبت مختبريًا)؛ إنشاء وسائط غير موثّق على Standard دون دور وسائط؛ RCE؛ سرقة جلسة المسؤول.
ثبّتت Drupal 11.4.8 بعد SA-CORE-2026-008. القفزة الأولى مقيّدة:
$patterns = \array_map(static fn ($hostPattern) => \sprintf('{%s}i', $hostPattern), Settings::get('media_oembed_discovery_trusted_host_patterns', []));
// ...
if (!$allowed) {
return FALSE;
}
$response = $this->httpClient->get($url);
return $this->findUrl($xpath, 'json') ?: $this->findUrl($xpath, 'xml');
تُرجع findUrl قيمة href الخام. وتقوم ResourceFetcher بجلبها عبر GET مع مهلة زمنية، ولا شيء غير ذلك:
$result = $xpath->query("//link[@type='application/$format+oembed']");
return $result->length ? $result->item(0)->getAttribute('href') : FALSE;
$response = $this->httpClient->request('GET', $url, [
RequestOptions::TIMEOUT => $this->timeout,
]);
المختبر هو أداة مطابقة لتلك الدوال في 11.4.8، وليس تثبيتًا حيًّا لموقع drupal:11.4.8. السطر الأخير يستخدم how=gadget. الأنماط الفارغة لم تُنفّذ الجلب. والمضيف غير المطابق لم يُنفّذ الجلب. مع السماح بـ ^firsthop$، أعاد discoverResourceUrl القيمة http://127.0.0.1:18080/oembed، وأعاد طلب GET غير المقيّد بالنطاق الشاهد.
الأخطاء التي سُجّلت سابقًا: اعتبار التعطيل الافتراضي إخفاقًا (فهو بوابة القفزة الأولى المقصودة؛ وما تبقّى هو القفزة الثانية)؛ ووصف تثبيت Standard الحي بأنه المرجع الحاسم (فهذه الحزمة هي دوال التثبيت)؛ والادعاء بوجود RCE لأن Guzzle نُفّذ. المسرح: صدفة عكسية. المرجع الحاسم هو التعطيل الافتراضي، إضافةً إلى حجب المضيف الخاطئ، إضافةً إلى شاهد القفزة الثانية.
البقايا نفسها من التثبيت: تخطّي CKEditor 5 FileIsImage.
python3 drupal-oembed-ssrf-Abraxas-Labs.py
أو
cd lab
./run.sh
الاسترجاع المحلي فقط: HTML الخاص بالقفزة الأولى على http://127.0.0.1:19510. القفزة الثانية هي http://127.0.0.1:18080/oembed داخل gadget netns.
SUCCESS drupal-oembed-ssrf how=gadget pin=11.4.8 first-hop=allowed second-hop=127.0.0.1:18080 default-off=blocked DRUPAL-OEMBED-SSRF-WITNESS
طبّق سياسة المضيف / عنوان IP الخاص نفسها على نقطة النهاية المكتشفة وعلى thumbnail_url. لا تجلب أي href كان مضيفه سيفشل في القفزة الأولى.
61044508071749629aaf3380aa85971e94df9e84)UrlResolver.php · ResourceFetcher.phpGNU Affero GPL v3.0. راجع LICENSE. مختبر loopback فقط. دون أي ضمان.