- Das
--von Git ist kein allgemeiner Optionsabschluss, sondern trennt Revisionen und Pathspecs. Um nicht vertrauenswürdige Revisionen sicher zu übergeben, ist daher--end-of-optionsnötig, das seit Git 2.24.0 unterstützt wird - In
git log --end-of-options "$rev" -- "$path"trennt die vordere Markierung Optionen und Revisionen, das hintere--trennt Revisionen und Pfade; beide sind nicht austauschbar - Selbst wenn ohne Shell direkt ein
argv-Array ausgeführt wird, kann CWE-88 Argument Injection auftreten, wenn Eingaben mit führendem Bindestrich als Optionen wie--upload-pack,core.sshCommandoderProxyCommandinterpretiert werden - Von 19 untersuchten Paketmanagern führten 17 das Git-Binary standardmäßig oder ausschließlich aus, aber das einzige Tool mit
--end-of-optionswar Gocmd/go - Eine grundlegende Abhilfe bringt Kompatibilitätskosten mit sich, weil je nach Unterbefehl die minimale Git-Version auf 2.24.0, 2.30.0 oder 2.43.1 angehoben werden muss; Git-Bibliotheken beseitigen zwar die Grenze für Argument Injection, müssen dafür aber Upstream-Fixes zur Checkout-Sicherheit direkt nachverfolgen
Der Unterschied zwischen -- und --end-of-options
- Bei üblichen Unix-Tools markiert
--das Ende der Optionsauswertung, daher behandeltrm -- -f-fals Dateinamen statt als Option zum erzwungenen Löschen - Git verwendet
--schon seit Langem als Trennzeichen zwischen Revisionen und Pathspecsgit log fooist mehrdeutig: Es kann einen Branch namensfoooder eine Datei meinengit log main -- README.mdmeint die Commits vonmain, dieREADME.mdverändert haben
- Wegen dieses Designs gab es an der Revisionsposition keinen Marker zum Beenden von Optionen, und wenn in
git log "$rev"$revmit einem Bindestrich beginnt, interpretiert Git es als Option - Der Commit zur Einführung von
--end-of-optionserklärt, dass das bestehende--bereits Revisionen und Pathspecs trennt und deshalb ein eigener Marker nötig ist, um Optionen und Revisionen zu unterscheiden --end-of-optionsist in gitcli(7) dokumentiert und wurde im November 2019 mit Git 2.24.0 hinzugefügt
Korrekte Verwendung je nach Befehl
git clone -- "$url"folgt beicloneder POSIX-Konvention, daher beendet--vor der URL die Optionsauswertung- Das hintere
--ingit checkout "$ref" --kennzeichnet$refals Revision statt als Dateinamen, verhindert aber nicht, dass$refvorher als Option interpretiert wird - Um nicht vertrauenswürdige Revisionen und Pfade gemeinsam sicher zu übergeben, müssen beide Marker verwendet werden, wie in
git log --end-of-options "$rev" -- "$path"--end-of-optionstrennt Optionen und Revisionen--trennt Revisionen und Pfade
- Wenn man beide Marker als austauschbar behandelt, lassen sich Eingaben mit führendem Bindestrich nicht blockieren
Unterschiedliche Unterstützung je nach Unterbefehl
- Die Unterstützung für
--end-of-optionswurde nicht auf einmal auf alle Git-Befehle angewendet, sondern pro Unterbefehl ergänzt git rev-parseverwendet einen eigenen Argument-Parser und unterstützt es erst ein Jahr nach der ursprünglichen Einführung, ab Git 2.30.0git checkoutundgit resetwerten--selbst aus und lehnten--end-of-optionsab, weil die frühe Implementierung es in der Argumentliste stehen ließ- Dieses Problem wurde im Februar 2024 mit Git 2.43.1 behoben
Argument Injection auch ohne Shell
- Git, Mercurial und SSH besitzen offiziell unterstützte Optionen, mit denen der Aufrufer bestimmte Befehle ausführen lassen kann
git clone --upload-pack=<cmd>legt das serverseitige Binary fest-c core.sshCommand=<cmd>ändert bei allen Git-Aufrufen den Verbindungsbefehl--config=alias.<subcmd>=!<shell>von Mercurial überschreibt den auszuführenden Unterbefehl mit einem beliebigen Shell-Skript-oProxyCommand=<cmd>von SSH gibt einen Proxy-Befehl an
- Wenn Wrapper-Programme nicht vertrauenswürdige Strings in die Argumentliste aufnehmen, werden diese Funktionen zu Angriffsvektoren
- Dieser Fehlertyp entspricht CWE-88, also Argument Injection, und unterscheidet sich von Shell Command Injection
- Er tritt auch dann auf, wenn ein Programm statt
system()einargv-Array undexecverwendet - Das Array wird unbeschädigt an Git übergeben, aber Git interpretiert Argumente mit führendem Bindestrich als Optionen
- Er tritt auch dann auf, wenn ein Programm statt
docker buildCVE-2019-13139 ist ein Beispiel ohne Shell, das Goos/execund einargv-Array nutzte- Das Fragment
#ref:direiner Git-Kontext-URL wurde angit fetch origin <ref>weitergegeben, worauf<ref>als--upload-pack=<cmd>interpretiert wurde
- Das Fragment
Wiederkehrende Schwachstellen in mehreren Versionsverwaltungssystemen
- Am selben Tag im August 2017 wurde dasselbe Muster in vier Versionsverwaltungssystemen veröffentlicht
- Git: CVE-2017-1000117
- Mercurial: CVE-2017-1000116
- Subversion: CVE-2017-9800
- CVS: CVE-2017-12836
- Alle vier Systeme übergaben Hostnamen aus URLs als SSH-Argumente, und Hostnamen, die mit
-oProxyCommand=begannen, wurden als SSH-Optionen behandelt - Laut der Phabricator-Nachanalyse fügte von den damals aktiv gepflegten drei Tools nur Subversion vor dem Hostnamen ein
--ein- Git und Mercurial validierten stattdessen das Hostnamenformat, auch weil nicht jede SSH-Implementierung
--unterstützt
- Git und Mercurial validierten stattdessen das Hostnamenformat, auch weil nicht jede SSH-Implementierung
- Code ohne
--wirkt und funktioniert normal, solange kein Argument mit einem Bindestrich beginnt; dieser Mechanismus ist daher standardmäßig unsicher
Wie Paketmanager exponiert werden
- Paketmanager übernehmen Git-URLs oder Refs aus Manifesten, Lockfiles und Metadaten transitiver Abhängigkeiten und reichen sie an Unterprozesse weiter
gem 'foo', git: '...'in Gemfilegithub:user/repo#refinpackage.json- entsprechende Konfigurationen in
pyproject.toml,Cargo.toml,mix.exs,Package.swift,pubspec.yaml,conanfile.pyundgo.mod
- Zum Stand von HEAD im Juli 2026 führten 17 von 19 untersuchten Paketmanagern das Git-Binary standardmäßig oder ausschließlich aus
- Die verbleibenden zwei verwenden standardmäßig Bibliotheken
- Cargo nutzt libgit2 und startet einen Git-Prozess, wenn
net.git-fetch-with-cliaktiviert ist - Poetry ist ab 1.2.0 auf dulwich umgestiegen und kann per Einstellung
system-git-clientdas System-Git nutzen
- Cargo nutzt libgit2 und startet einen Git-Prozess, wenn
- Nix verwendet beim Lesen lokaler Repositories libgit2, startet aber für Fetches einen Git-Prozess, weil libgit2 keine
git-credential-Hilfsprogramme unterstützt - Untersucht wurden Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg und Yarn
Bestätigte CVEs in Paketmanagern
- Zu den veröffentlichten Schwachstellen dieses Typs in Paketmanagern gehören folgende Fälle
- Bundler: CVE-2021-43809
- Composer: CVE-2021-29472, CVE-2022-24828
- Poetry: CVE-2022-36069
- pip: CVE-2023-5752
- CocoaPods: CVE-2022-21223, CVE-2022-24440
- Go: CVE-2025-68119
- Snyk, das mehrere Schwachstellen von 2022 gefunden hatte, veröffentlichte eine Studie zu Argument Injection in Git und Mercurial
- Sonar pflegt eine Liste gefährlicher Optionen nach Binary
Tatsächlicher Stand der Abwehr und der Go-Fix
- Von den 17 Paketmanagern, die einen Git-Prozess starten, verwendete nur Go
cmd/go--end-of-options - Go fügte im Juni 2019 im Rahmen einer allgemeinen Härtung vor Repository-URLs ein
--ein - Im Januar 2026 stellte sich heraus, dass
--allein nicht ausreicht; der Fix für CVE-2025-68119 fügte daraufhin breit--end-of-optionshinzu - Derselbe Fix enthält auch
HGPLAIN=+strictflags- Diese Einstellung schränkt seit Mercurial 4.4.2 von 2017 die frühe Optionsauswertung von Mercurial ein
- Der Go-Fix-Commit merkt an, dass möglicherweise strukturellere Änderungen nötig sind, damit dasselbe Problem nicht wieder eingeführt wird, behebt aber zunächst das aktuelle Problem
Die meisten Schutzmaßnahmen kamen erst nach Offenlegung
- Die übrigen Paketmanager schützen ihre Argumentlisten, falls überhaupt, meist mit
--oder durch Ablehnung führender Bindestriche in Eingaben - Das
--vor dergit clone-URL in Bundler wurde mit dem Patch für CVE-2021-43809 hinzugefügt - Die Ablehnung führender Bindestriche in cocoapods-downloader wurde innerhalb von zehn Tagen im März 2022 in drei Commits umgesetzt, zeitlich passend zur Offenlegung von CVE-2022-21223
- Die Schutzmaßnahme von Poetry wurde im September 2021 hinzugefügt, ein Jahr später erhielt sie eine CVE, und sechs Monate danach erfolgte der Wechsel zu dulwich
- vcpkg ist eine Ausnahme und nutzte vom ersten Tag der Git-Registry-Unterstützung an
--
Kompatibilitätsgrenzen durch minimale Git-Versionen
- Die Warnmeldung zu Composer CVE-2022-24828 nennt
--end-of-optionsals korrekten Fix, lehnt aber Branch-Namen mit führendem Bindestrich ab, weil auch ältere Git-Versionen unterstützt werden müssen - Die Git-Integration von vcpkg nennt Git 2.7.4 als Mindestversion, und
HOMEBREW_MINIMUM_GIT_VERSIONfür Linux in Homebrew ist seit 2018 auf 2.7.0 gesetzt - Da Amazon Linux 2 mit Git 2.14.3 im Juni 2026 das Lebensende erreichte, fallen Distributionen, an deren Versionen sich diese Untergrenzen orientierten, erst jetzt aus dem Supportbereich heraus
- Auch der LTS-Status von Ubuntu erschwert einen einheitlichen Wechsel
- Ubuntu 18.04 liefert Git 2.17.0 und erhält erweiterten Support bis 2028
- Ubuntu 20.04 liefert Git 2.25.1 und erhält erweiterten Support bis 2030
- Git 2.25.1 akzeptiert
--end-of-optionsbeigit fetch, lehnt es aber beigit rev-parseab
- Wer sich auf
--end-of-optionsstützen will, muss für die meisten Unterbefehle Git 2.24.0, fürrev-parse2.30.0 und fürcheckoutsowiereset2.43.1 als Mindestversion verlangen - Höhere Mindestversionen bedeuten, dass Nutzer mit älteren, von Distributionen mitgelieferten Git-Versionen nicht mehr unterstützt werden können
Git-Bibliotheken statt Prozessausführung
- libgit2, gitoxide, go-git, JGit und dulwich implementieren die für Clone und Fetch nötigen Git-Transportprotokolle innerhalb des Prozesses
- Es gibt keine separate
argv-Grenze, also kein Ziel mehr, in das eine Argumentliste injiziert werden könnte - Jujutsu verwendet für die Git-Integration gitoxide und hat keine veröffentlichten CVEs dieses Typs
- Die bislang zwei Advisories betreffen Path Traversal und eine aus der Bibliothek übernommene fehlende Prüfung auf SHA-1-Kollisionen
- CVE-2025-21613 in go-git ist auf den
file://-Transport beschränkt- Das ist dort der einzige Codepfad, auf dem go-git überhaupt das Git-Binary ausführt
- Wer eine eigene Git-Implementierung mitliefert, muss sämtliche Upstream-Fixes zur Checkout-Sicherheit nachverfolgen; sowohl libgit2 als auch JGit mussten dazu wiederholt nachbessern
- Diese Kosten sind real, aber das Problem verschiebt sich von dauerhaft nötiger Argumentprüfung an jedem Aufrufpunkt hin zur Anwendung konkreter Upstream-Patch-Flüsse
Vorgeschlagener Änderungsumfang für Homebrew
- Der Homebrew-PR hebt die minimale Git-Version auf 2.30.0 an und fügt
--end-of-optionsan folgenden Stellen hinzu- vor URLs bei
clone,remote set-urlundls-remote - vor Refs bei
rev-parse
- vor URLs bei
- Aufrufe von
checkoutundresetwerden nicht geändert- Um auch diese beiden Befehle zu schützen, wäre Git 2.43.1 nötig, veröffentlicht im Februar 2024
- Diese Version ist neuer als das Git, das mehrere aktuell unterstützte Distributionen bereitstellen
1 Kommentare
Meinungen auf Lobste.rs
Besonders lässt sich Wissen, das man bei einem
jj-Befehl gelernt hat, ganz natürlich auf andere Befehle übertragen. Diegit log-Dokumentation ist voll von wuchernden Flags je Ausgabeoption, vermischt mit der Commit-Range-Syntax der „speziellen Notationen“ und zusätzlichen Filter-Flags; außerdem überträgt sich nur wenig davon auf andere Git-Befehle.Die
jj log-Dokumentation ist dagegen so knapp, dass selbst auf einer Seite noch Platz bleibt. Die zusammengewürfelte Komplexität von Git wird durch drei Dinge ersetzt: Revision Sets, File Sets und eine Output-Template-DSL. Sie werden in jj durchgängig konsistent verwendet und sind dadurch viel einfacher, kombinierbarer und intuitiver. Man muss berücksichtigen, dass Git über 20 Jahre Ballast angesammelt hat, aber jj scheint deutlich besser aufgestellt, um das zu vermeiden.--braucht, ist mir klar; wenn dafür aber auch noch eigene Transformationsregeln verlangt werden, wird es zu einer viel zu gefährlichen Falle.--gewählt wurde, um die Mehrdeutigkeit von Dateiargumenten aufzulösen. Gab es einen nicht offensichtlichen Design-Trade-off, der dafür sprach, statt eines einfacheren Ansatzes mit expliziten Dateiargumenten?Blickt man auf die Debatten zwischen Tcl und Scheme in den 1990ern zurück, könnte in diesem Fall „schlechter ist besser“ tatsächlich gestimmt haben. Während sich die Familie der S-Ausdrücke, einschließlich JSON und XML und Ähnlichem, im UNIX-Ökosystem kaum durchgesetzt hat, hatte Tcl einen vergleichsweise prinzipientreuen Ansatz, Untersprachen innerhalb von Strings zu schichten.