إثبات مفهوم ومختبر لثغرة SSRF في Gitaly fetch الخاصة بـ GitLab CE 19.4.1: يتخطى مستوردو GitHub/Bitbucket تثبيت resolved_address، مما يسمح لمستخدم مُصادَق عليه بإعادة ربط DNS وجلب المضيفات الداخلية.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-gitaly-fetch-ssrf
GitLab CE 19.4.1 - GitLab Inc.
استيراد GitHub Enterprise / Bitbucket Server (و Direct Transfer، عندما يكون هذا العلم مفعّلًا) يتخطى Projects::ImportService#get_resolved_address. الإعداد الافتراضي لـ Repository#fetch_as_mirror هو resolved_address: "". يقوم Gitaly FetchRemote / CreateRepositoryFromURL بحل اسم مضيف الاستنساخ مرة أخرى. كان UrlBlocker قد سمح مسبقًا بسجل A عام. أعد الربط، وستهبط كائنات git-HTTP في المشروع المستورد.
مستخدم مسجّل الدخول يمكنه تشغيل استيراد GitHub Enterprise يمكنه جعل Gitaly يقوم بـ git-fetch لمضيف لم يعد هو عنوان IP الذي ثبّته UrlBlocker.
| ID | لا يوجد CVE بعد |
| CWE | CWE-918، CWE-367 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N |
| Product | GitLab CE |
| Affected | 19.4.1-ce.0 (26212baacadb على شجرة v19.4.1-ee) |
| Auth | مستخدم موثّق، مستورد GitHub Enterprise / Bitbucket Server |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 فقط |
وجّه استيراد GitHub Enterprise إلى اسم مضيف يتحكم به. يسمح Rails UrlBlocker.validate! بسجل A عام. قيمة ParallelImporter.imports_repository? صحيحة، لذا لا يقوم ImportService أبدًا بالتثبيت. يستدعي RepositoryImporter#fetch_as_mirror خدمة Gitaly بـ resolved_address فارغ. يقوم Gitaly بـ git HTTP-fetch لأي شيء يشير إليه ذلك الاسم الآن. يحتوي المستودع المستورد على الكائنات.
على omnibus الكل-في-واحد يكون ذلك هو loopback الخاص بالعامل. تفشل أسماء مضيفات الاستنساخ المباشرة http://127.0.0.1 بشكل مغلق. استيراد المستودع عبر URL يقوم بالتثبيت بالفعل. يحتاج Direct Transfer إلى bulk_import_enabled (القيمة الافتراضية في self-managed هي false).
نفس المنتج، بقايا شقيقة: Gitea HTTPS import pin drop، webhook 0.0.0.0/8.
قرأت ملاحظات تصحيح 19.4.1، ثم المستوردين. يقوم Projects::ImportService#get_resolved_address بإعادة كتابة اسم المضيف إلى عنوان IP الذي تم التحقق منه ويمرره إلى Gitaly. يقوم المستوردون المتوازيون لـ GitHub و Bitbucket Server بتعيين imports_repository? وتخطي ذلك المسار.
project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)
الإعداد الافتراضي لـ fetch_as_mirror هو resolved_address: "". حتى أن repository_pipeline.rb الخاص بـ Direct Transfer يستدعي validate! ويتجاهل القيمة المُرجعة.
قمت بتشغيل gitlab/gitlab-ce:19.4.1-ce.0 الجاهز. يبدأ import_sources في CE فارغًا؛ يفعّل المختبر GitHub. POST /api/v4/import/github مع github_hostname=http://ghe.lab:18080. الـ catcher في netns الخاص بـ GitLab يتحدث JSON مزيفًا لـ GHE بالإضافة إلى git http-backend. كتلة المستودع الأولية WITNESS هي GITLAB-GITALY-FETCH-SSRF-WITNESS.
أعاد GET /api/v4/projects/1/repository/files/WITNESS/raw?ref=master الشاهد. سجل Gitaly: FetchRemote grpc.code=OK على root/ghe-ssrf. الـ catcher: git/2.55 info/refs و git-upload-pack. القيمة السلبية github_hostname=http://127.0.0.1:18080 هي 400 Invalid URL.
المنعطفات الخاطئة المسجّلة مسبقًا: البذرة CveLabRoot9! مرفوضة بسبب WeakPasswords (سلسلة فرعية root)؛ 19.4 يحشو ما بعد ذلك الفحص عند 64 حرفًا؛ لا يستطيع Faraday الاتصال بعنوان IP عام ليس هو الـ catcher، لذا تقوم الأداة بإعادة توجيه REDIRECT لـ 1.1.1.1:18080 بينما لا يزال UrlBlocker يرى سجل A عام؛ يمكن لـ git-HTTP أن يفوز قبل أن يقلب GET /repos/org/repo نظام DNS؛ يعطي GitLab خطأ 502 في منتصف الاستيراد ثم يكون الملف موجودًا على أي حال. reverse shell. مسرح. الـ oracle هو الكتلة المستوردة بالإضافة إلى Gitaly FetchRemote.
cd lab
./run.sh
الهدف فقط http://127.0.0.1:18410. يحتاج GitLab CE إلى عدة جيجابايت من RAM. مختبر مصرّح به فقط.
negative-loopback status=400 Invalid URL
witness-import status=201 import_status=scheduled
file-final http=200 body='GITLAB-GITALY-FETCH-SSRF-WITNESS'
SUCCESS GITLAB-GITALY-FETCH-SSRF imported-witness git-fetch-no-pin GITLAB-GITALY-FETCH-SSRF-WITNESS
def import_repository
project.ensure_repository
refmap = Gitlab::GithubImport.refmap
project.repository.fetch_as_mirror(project.unsafe_import_url, refmap: refmap, forced: true)
مرّر get_resolved_address إلى fetch_as_mirror بالطريقة التي يفعلها استيراد المستودع عبر URL بالفعل. لا تتجاهل UrlBlocker.validate! في Direct Transfer أيضًا.
repository_importer.rb · repository.rb · import_service.rbGNU Affero GPL v3.0. راجع LICENSE. مختبر loopback فقط. لا ضمان.