
Forgejo 16.0.5 - عالية - عن بُعد - SSRF - hostmatcher يسمح بـ 0.0.0.1 كعنوان IP عام
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-hostmatcher-0001-ssrf
الفئة: SSRF الوصول: عن بُعد
Forgejo 16.0.5 (8d24b7d) - Forgejo
القيمة الافتراضية لـ [webhook] ALLOWED_HOST_LIST فارغة، لذا يبني services/webhook/deliver.go العميل باستخدام external المدمج. هذا المُحدِّد هو Go IsGlobalUnicast() && !isPrivate(). الدالة isPrivate تشمل RFC1918/4193 بالإضافة إلى إضافة CGNAT في v16.0.2. النطاق 0.0.0.0/8 وفق RFC 6890 ليس أيًّا من تلك. 127.0.0.1 هو loopback ومرفوض. 0.0.0.1 مسموح.
يمكن لمستخدم مسجَّل الدخول إرسال webhook إلى http://0.0.0.1 وقراءة جسم HTTP من سجل الـ hook. 127.0.0.1 على نفس المنفذ مرفوض.
| المعرّف | لا يوجد CVE بعد |
| الفئة | SSRF |
| الوصول | عن بُعد (مُصادَق عليه؛ أي مستخدم يمكنه إنشاء webhook لمستودع) |
| CWE | CWE-918 |
| CVSS | مرتفع: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| المنتج | Forgejo |
| المتأثر | حتى v16.0.5 (8d24b7d)؛ من بقايا إضافة CGNAT في v16.0.2 |
| المصادقة | مستخدم مسجَّل الدخول يمكنه إنشاء webhook لمستودع |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
سجّل في تثبيت افتراضي، أنشئ مستودعًا، اضبط webhook في forgejo على http://0.0.0.1:<port>/...، اختبره، واقرأ جسم الاستجابة.
| الصلاحية | الطريقة / نقطة الوصول | الاستطلاع / السلامة / التوافر | مُثبَت مختبريًا أم مُستنتَج |
|---|---|---|---|
الاتصال بـ 0.0.0.1 الخاص بهذه الشبكة متجاوزًا قائمة السماح الافتراضية للـ webhook | POST /api/v1/repos/{owner}/{repo}/hooks ثم POST .../hooks/{id}/tests مع URL http://0.0.0.1:8080/ssrf | استطلاع (HTTP صادر من مضيف Forgejo) | مُثبَت مختبريًا. السجل خزّن جسم HTTP 200 FORGEJO-HOSTMATCHER-0001-WITNESS |
| قراءة استجابة HTTP الكاملة لأيًّا كان ما ردّ | GET /{owner}/{repo}/settings/hooks/{id} يطبع ResponseInfo.Body | استطلاع (SSRF بالاستجابة الكاملة) | مُثبَت مختبريًا. نفس الشاهد في صفحة السجل |
نفس الفئة عبر IPv4-mapped [::ffff:0.0.0.1] | نفس المُطابِق To4() ثم checkIP | استطلاع | مُستنتَج من hostmatcher. المختبر استخدم 0.0.0.1 |
| الوصول إلى مستمع ارتبط بنطاق أوسع من loopback في netns الخاص بـ Forgejo | connect() في Linux إلى 0.0.0.0/8 هو هذا المضيف بمجرد أن يصبح 0.0.0.1 على lo | استطلاع | مُثبَت مختبريًا مقابل oracle الجانبي. ليس ادعاءً حول IMDS سحابي محدد |
مغلق على نفس المُطابِق الافتراضي: 127.0.0.1 (مرفوض مُثبَت مختبريًا في السجل)، 127.0.0.0/8، RFC1918، CGNAT 100.64.0.0/10، link-local 169.254.0.0/16 بما في ذلك IPv4 IMDS، غير المحدد 0.0.0.0. الـ webhook الصادر لا يكتسب X-Forgejo-Internal-Auth / INTERNAL_TOKEN. هذا ليس RCE بحد ذاته.
هذا ليس إصلاح CGNAT في v16.0.2. لم يُنمِّ Forgejo أبدًا قائمة شبكات محجوزة على نمط Gitea. نفس فئة gitea-hostmatcher-0000-ssrf.
وضع v16.0.2 نطاق CGNAT في private. التصحيحات التي تضيف قائمة رفض هي هدية: تقرأ القائمة، ثم تسأل عمّا لا يزال لدى IANA مما نسيته القائمة. ادّعى بحث شقيق أن IsGlobalUnicast في Go 1.26 أغلق 0.0.0.1. تحققت من المُحدِّد ومن الاختبارات داخل الشجرة. لم تغطِّ 0.0.0.1 أبدًا.
المنعطفات الخاطئة المسجَّلة مسبقًا: اعتبار إنشاء webhook بـ HTTP 201 هو الـ oracle (السجل هو الـ oracle)؛ نجاح تسليم 127.0.0.1 (سيكون ذلك خطأً مختلفًا - المختبر يتطلب رفضه)؛ ضرب /api/internal بـ X-Forgejo-Internal-Auth؛ reverse shell. مسرحيات.
python3 forgejo-hostmatcher-0001-ssrf-Abraxas-Labs.py
أو:
cd lab
./run.sh
الهدف فقط http://127.0.0.1:19320. الصورة codeberg.org/forgejo/forgejo:16.0.5. الـ oracle الجانبي يتشارك netns الخاص بـ Forgejo على المنفذ 8080.
create-hook name=neg-loopback ...
negative-control 127.0.0.1 blocked
create-hook name=hit-0001 ...
poll id=2 witness=True
SUCCESS forgejo-hostmatcher-0001-ssrf how=live pin=v16.0.5 0.0.0.1:8080 HTTP 200 FORGEJO-HOSTMATCHER-0001-WITNESS
تعامل مع RFC 6890 0.0.0.0/8 على أنه ليس خارجيًا (قائمة شبكات محجوزة، وليس IsGlobalUnicast). يجب رفض 0.0.0.1 بنفس طريقة رفض 127.0.0.1.
hostmatcher.goGNU Affero GPL v3.0. انظر LICENSE. مختبر loopback فقط. لا ضمان.