Argument-Injection bei der Verwendung von VCS-Tools neu betrachtet – Git und Mercurial
Alessio Della Libera
23. August 2022
0 Min. LesezeitEines der Hauptziele dieser Untersuchung war es, zu ergründen, wie sich trotz Verwendung einer sicheren API, die Command Injection verhindert, beliebige Befehle ausführen lassen. Im Fokus stehen Tools für Versionskontrollsysteme (VCS) wie git und hg (Mercurial), bei denen sich unter bestimmten Umständen über einige Optionen beliebige Befehle ausführen lassen.
Ziel dieser Untersuchung sind Webanwendungen und Bibliotheksprojekte, die in beliebigen Programmiersprachen geschrieben sind und diese Befehle über eine sichere API aufrufen. CLI-Projekte oder Projekte, die die Befehle mit Daten aus gespeicherten Quellen wie Konfigurationsdateien aufrufen, haben wir nicht untersucht.
In diesem Blogbeitrag zeigen wir typische Szenarien, in denen sich trotz Verwendung einer sicheren API über die Injection von Argumentoptionen beliebige Befehle ausführen lassen. Außerdem stellen wir Beispiele zur Behebung und Empfehlungen für Open-Source-Maintainer bereit.
Hintergrund zur Argument-Injection
Stellen Sie sich folgendes Szenario vor: Ein Entwickler möchte aus seinem Projektverzeichnis git- oder hg-Befehle aufrufen, um mit Repositories zu interagieren. Dabei werden jedoch nutzergesteuerte Werte als Parameter an diese Befehle übergeben, sodass Nutzer beispielsweise die Repository-URL oder den Branch-Namen angeben können. Da dem Entwickler die Schwachstelle Command Injection bekannt ist, entscheidet er sich für eine sichere API zur Ausführung von Shell-Befehlen. Befindet sich das Projekt beispielsweise in Node.js, verhindert die API spawn aus dem Modul child_process Command Injection, wenn sie folgendermaßen aufgerufen wird:
Das ist jedoch nicht immer der Fall. Wie wir gleich sehen werden, hängt es auch davon ab, welcher Befehl von diesen APIs aufgerufen wird. Wenn Sie mehr darüber erfahren möchten, was Argument Injection ist, finden Sie in diesem Artikel von Staaldraad eine hervorragende Quelle.
Warum sollten wir uns für Git interessieren?
Einige Git-Unterbefehle bieten Optionen, mit denen Nutzer festlegen können, welche Programme oder Befehle ausgeführt werden. Ein Beispiel dafür ist die Option --upload-pack, die für folgende Git-Unterbefehle verfügbar ist:
Betrachten wir beispielsweise die Unterbefehle git fetch, ls-remote und pull, die die Option --upload-pack unterstützen.
Je nach Befehl sind bestimmte Argumente erforderlich, auf die optionale Argumente folgen. git fetch erfordert beispielsweise mindestens ein Argument, also ein Repository, gefolgt von refspec und einer Reihe von Optionen.
Wenn Sie die folgenden Befehle in einem Terminal ausführen, wird der Shell-Befehl touch HELLO ausgeführt:
Wie sieht verwundbarer Code bei „git“ aus?
Um diese Schwachstelle zu veranschaulichen, verwende ich JavaScript und Python. Der Kürze halber nehmen wir an, dass die Werte url und branch von Nutzern kontrolliert und nicht ordnungsgemäß bereinigt werden.
Das folgende Beispiel zeigt, dass sich weiterhin beliebiger Code ausführen lässt, selbst wenn eine sichere API verwendet wird, die vor klassischen Command-Injections schützt:
JavaScript (Node.js)
Die letzte Zeile zeigt, dass eine gewöhnliche Command-Injection-Payload – also eine Payload mit Sonderzeichen wie ; & | usw. zum Verketten von Befehlen – nicht funktioniert.
Python
Wichtig ist, dass die oben beschriebenen Szenarien zur Befehlsausführung führen können, wenn die erforderlichen Argumente für die entsprechenden Git-Unterbefehle angegeben werden. Wird dem Unterbefehl git ls-remote beispielsweise nur ein Argument und kein weiteres Argument übergeben, funktionieren die vorherigen Szenarien nicht:
JavaScript (Node.js)
Der Grund dafür ist, dass das einzige angegebene Argument als Repository interpretiert wird (bei git ls-remote). Wird dem Befehl ein weiteres gültiges Argument übergeben, befinden wir uns wieder im vorherigen Szenario und können weiterhin Code ausführen.
Empfehlungen zur Behebung
Eine mögliche Lösung für die oben beschriebenen Szenarien ist, vor nutzergesteuerten Werten -- einzufügen. Wie in der offiziellen Dokumentation erläutert wird: „Wenn Sie ein Skript schreiben, das beliebige Nutzereingaben verarbeiten soll, empfiehlt es sich, durch das Einfügen von trennenden -- an den passenden Stellen eindeutig zu kennzeichnen, welche Argumente was sind.“
Der folgende Code führt beispielsweise nicht mehr zur Befehlsausführung:
Warum sollten wir uns für hg (Mercurial) interessieren?
Wie wir im vorherigen Abschnitt gesehen haben, müssen bestimmte Bedingungen erfüllt sein, damit beliebige Befehle ausgeführt werden können (z. B. erforderliche Argumente). Bei hg ist das jedoch nicht der Fall – dadurch lässt sich die Schwachstelle viel einfacher ausnutzen.
Unter den zahlreichen verfügbaren Optionen können Nutzer in Mercurial für jeden Befehl (clone, init, log usw.) aliases und hooks festlegen. Dazu dient die Option –config.
Wie in der Mercurial-Dokumentation erläutert wird, „können Sie mit Aliases eigene Befehle auf Basis anderer Befehle (oder Aliases) definieren und optional Argumente hinzufügen. Ein Alias kann mit einem Ausrufezeichen („!“) beginnen und wird dann zu einem Shell-Alias. Ein Shell-Alias wird in der Shell ausgeführt und ermöglicht Ihnen, beliebige Befehle auszuführen. Zum Beispiel:
Damit können Sie "hg echo foo" ausführen, um "foo" in Ihrem Terminal auszugeben.“
Hooks sind hingegen „Befehle oder Python-Funktionen, die automatisch durch verschiedene Aktionen ausgeführt werden, etwa beim Starten oder Abschließen eines Commits.“ Sie werden ausgeführt, sobald bestimmte Bedingungen erfüllt sind, und können vor (pre-<command>) oder nach (post-<command>) anderen Mercurial-Befehlen laufen.
Wie sieht verwundbarer Code bei Verwendung von „hg init“ aus?
Sehen wir uns ein Beispiel an, wie sich beliebige Befehle ausführen lassen, wenn der Befehl hg init mit nicht bereinigten Nutzereingaben aufgerufen wird.
Beispiel für einen Alias
Der Befehl touch HELLO wird ausgeführt. Das liegt daran, dass ein Alias für den Befehl init definiert ist und anstelle des Befehls hg init ausgeführt wird.
Wird derselbe Befehl im Code ausgeführt, sieht er folgendermaßen aus (wie zuvor nehmen wir an, dass die source vom Nutzer kontrolliert und nicht ordnungsgemäß bereinigt wird):
JavaScript (Node.js)
Python
Beispiel für Hooks
Der Befehl touch HELLO wird vor der Ausführung des Befehls init ausgeführt (d. h. über die Option pre-<command>).
Wird derselbe Befehl im Code ausgeführt, sieht er folgendermaßen aus:
JavaScript (Node.js)
Python
Anders als bei Git müssen wir zur Ausführung beliebiger Befehle nur ein Argument kontrollieren, das an Mercurial-Befehle übergeben wird – selbst wenn es sich um den einzigen verwendeten Parameter handelt.
Empfehlungen zur Behebung
Auch dieses Problem lässt sich beheben, indem vor nutzergesteuerten Argumenten die Zeichen -- eingefügt werden.
Die folgende Ausnutzung funktioniert beispielsweise nicht mehr – stattdessen wird ein Repository-Ordner erstellt, dessen Name dem Inhalt der Variablen source entspricht:
Ergebnisse
Ähnliche Probleme wurden bereits in der Vergangenheit ausgenutzt. Bekannte Beispiele sind Remote-Code-Execution-Angriffe auf CocoaPods Trunk und Phabricator.
Die folgende Tabelle fasst die im Rahmen dieser Untersuchung gefundenen und offengelegten Probleme zusammen:
Projekt | Schwachstelle | Advisory/Referenz | CVE |
|---|---|---|---|
ungit | RCE | CVE-2022-25766 | |
mozilla/pontoon | RCE | k. A. | |
GoCD Server | RCE | CVE-2022-29184 | |
Weblate | RCE (x2) | CVE-2022-23915 | |
simple-git | Command Injection | CVE-2022-24433 | |
ruby-git | Command Injection | CVE-2022-25648 | |
cocoapods-downloader | Command Injection | CVE-2022-24440 | |
cocoapods-downloader | Command Injection | CVE-2022-21223 | |
Masterminds/vcs | Command Injection | CVE-2022-21235 | |
git-php | Command Injection | CVE-2022-25866 | |
libvcs | Command Injection | CVE-2022-21187 | |
workspace-tools | Command Injection | CVE-2022-25865 | |
cookiecutter | Command Injection | CVE-2022-24065 | |
hashicorp/go-getter | Command Injection | CVE-2022-26945 |
Nachdem wir diese Schwachstellen gefunden hatten, meldeten wir sie gemäß unserer Richtlinie zur Offenlegung von Schwachstellen vertraulich an die Maintainer. Ich möchte allen kontaktierten Maintainerinnen und Maintainer persönlich für ihre Zeit und Zusammenarbeit danken.
In den folgenden Fallstudien erläutern wir, wie sich in diesen bekannten Projekten Remote Code Execution und Command Injection ausführen ließen.
Fallstudie: Remote Code Execution in Weblate
Weblate ist ein „webbasiertes Lokalisierungstool mit enger Versionskontrollintegration“. Ein authentifizierter Nutzer konnte durch Argument-Injection Remote Code Execution erreichen.
Beim Hinzufügen neuer Übersetzungskomponenten und bei der Verwendung von Mercurial-Repositories wird die Nutzereingabe branch im Befehl hg pull ohne ordnungsgemäße Bereinigung verwendet.
Der relevante Code, der den Befehl hg pull mit nutzergesteuerten Werten aufruft, lautet:
Proof of Concept
Um beliebige Befehle auszuführen, muss der Nutzer in einem Übersetzungsprojekt eine New Translation Component erstellen und für Repository branch den Wert --config=alias.pull=!id>/app/cache/static/output_rce1.txt angeben. In diesem Fall wird der Befehl id ausgeführt und die Ausgabe in eine Datei umgeleitet, die unter http://localhost:8888/static/output_rce1.txt erreichbar ist.
1. Melden Sie sich bei der Anwendung an.
2. Fügen Sie ein neues Übersetzungsprojekt hinzu und klicken Sie anschließend auf Save.

3. Fügen Sie dem soeben erstellten Übersetzungsprojekt eine neue Übersetzungskomponente hinzu. Geben Sie im Feld Repository Branch die Payload --config=alias.pull=!id>/app/cache/static/output_rce1.txt ein und klicken Sie anschließend auf Continue.

4. Die Ausgabe des Befehls id ist unter http://localhost:8888/static/output_rce1.txt verfügbar.

Behebung
Wie Sie sehen, ließ sich RCE auch bei der Arbeit mit Git-Repositories erreichen. In diesem Fall wurde die nicht bereinigte Nutzereingabe repo an den Befehl git ls-remote übergeben.
Beide Probleme wurden vom Maintainer umgehend in Version 4.11.1 behoben (siehe GitHub-Release). Die Korrekturen für die Fälle Mercurial und Git fügten vor der Nutzereingabe die Zeichen -- ein, damit sie nicht als Argument interpretiert wird.
Fallstudie: Command Injection in ruby-git
Ruby git ist eine „Bibliothek, mit der sich Git-Repositories erstellen, lesen und bearbeiten lassen, indem Systemaufrufe an die Git-Binärdatei weitergeleitet werden“.
Beim Aufruf der Funktion fetch(remote = 'origin', opts = {}) wird der Parameter remote ohne Bereinigung an den Unterbefehl git fetch übergeben. Wie bereits erwähnt, akzeptiert git fetch als einer der git-Unterbefehle das Argument --upload-pack. Weitere Informationen zu --upload-pack finden Sie in der Dokumentation zu git-fetch.
Der relevante Code, der den Befehl git fetch mit nutzergesteuerten Werten aufruft, lautet:
Proof of Concept
Der folgende PoC zeigt, wie sich beliebige Befehle ausführen ließen:
Behebung
Das Problem wurde von den Maintainer:innen in Version 1.11.0 (GitHub-Release) behoben. Wie beim vorherigen Problem wurden durch den Fix die Zeichen -- vor benutzergesteuerte Werte eingefügt.
Die wichtigsten Erkenntnisse
… für Entwickler:innen
Wenn Sie Befehle über eine sichere API aufrufen, die Command-Injection verhindert und benutzergesteuerte Werte akzeptiert, sollten Sie darauf achten, dass diese Werte das Verhalten des Befehls nicht verändern, indem sie Optionen einschleusen oder manipulieren. Um solche Probleme zu vermeiden, können Sie die Zeichen -- verwenden, um Argumente von benutzergesteuerten Werten zu trennen.
… für Maintainer:innen
Wenn eine Funktion oder API Aufrufe von Befehlen kapselt, die benutzergesteuerte Werte akzeptieren, kann es sinnvoll sein, das Verhalten zu dokumentieren. So vermeiden Sie Verwirrung bei Nutzer:innen, die sehen, dass Maintainer:innen einige Probleme mit der Eingabebereinigung beheben (etwa Command-Injection durch die Verwendung einer sicheren API verhindern), andere aber nicht – zum Beispiel, wenn die Eingabe als gefährliche Option verwendet werden kann, die zu unerwartetem Verhalten führt.
… für Sicherheitsforscher:innen
Wir haben uns auf git- und hg-Befehle konzentriert, aber viele weitere verhalten sich ähnlich. Selbst bei der Verwendung sicherer APIs, die Command-Injection verhindern, können Sicherheitsprobleme auftreten. Das hängt ganz davon ab, welche Befehle ausgeführt werden. Wenn Sie ähnliche Probleme (oder andere Sicherheitslücken) in einem Open-Source-Projekt finden, das von unserem Programm unterstützt wird, melden Sie diese bitte über das Snyk-Formular zur Offenlegung von Sicherheitslücken.



