2 Punkte von GN⁺ 2024-05-26 | 1 Kommentare | Auf WhatsApp teilen
  • Die Checksum Database von Go und der öffentliche Module-Proxy akzeptieren auch Repositories ohne Go-Code, wodurch ein Pfad entsteht, beliebige Git-Repository-Daten in die Go-Infrastruktur einzuspeisen und später wieder herunterzuladen
  • Eine Anfrage an sum.golang.org/lookup/$module@$version holt bei einer noch nicht protokollierten Modulversion das Modul vom Origin-Server, wobei in diesem Prozess auch bei proxy.golang.org die ZIP-Datei des Repositories bereitgestellt wird
  • Das Ruby-Repository von Homebrew und ein Fork eines Rust-Repositories befanden sich in der Checksum Database, und ein Experiment zeigte erfolgreich, dass sich nicht nur neue Go-Module, sondern auch Repositories ohne Go-Dateien per Pseudo-Version registrieren lassen
  • Für Modul-ZIPs gilt sowohl komprimiert als auch unkomprimiert ein Limit von maximal 500 MiB, was dennoch groß genug ist, um Download-Beschränkungen auf Entwicklerrechnern und in CI/CD zu umgehen, Payloads zu speichern oder C2 zu implementieren
  • Von rund 1,59 Millionen eindeutigen Pfaden in sum.golang.org entfallen etwa 1,51 Millionen auf GitHub-Pfade, also rund 95 %, was sowohl die GitHub-Abhängigkeit des Go-Ökosystems als auch das Missbrauchspotenzial des öffentlichen Proxys offenlegt

In der Go-Checksum-Database gefundene Nicht-Go-Repositories

  • Beim Durchsehen der Checksum Database von Go fiel in der Tabelle modules auf, dass github.com/homebrew/homebrew-core sehr häufig vorkommt
    • github.com/homebrew/homebrew-core: 39.438
    • github.com/Homebrew/homebrew-core: 30.896
    • github.com/concourse/concourse: 25.372
    • github.com/openshift/release: 24.065
    • github.com/cilium/cilium: 22.138
  • Das Homebrew-Repository ist als Ruby-Repository bekannt, und weder im Repository noch in einer geklonten Kopie wurden go.mod oder Go-Quelldateien gefunden
  • Der Unterschied in der Groß-/Kleinschreibung lässt sich durch die Regeln zur Case Encoding in der Go-Dokumentation erklären
    • Großbuchstaben werden als ! plus entsprechender Kleinbuchstabe kodiert, sodass auf Dateisystemen ohne Unterscheidung von Groß- und Kleinschreibung sowohl example.com/M als auch example.com/m gespeichert werden können
  • Auch github.com/Edu4rdSHL/rust-headless-chrome taucht in der Checksum Database auf, obwohl es sich um einen nicht mit Go verbundenen Fork eines Rust-Repositories handelt

Wie /lookup Repositories abruft

  • Laut Go Modules Reference der Go-Modul-Dokumentation ruft der Go-Befehl beim Abfragen der Checksum Database zunächst die Datensätze über den Endpunkt /lookup ab
  • Wenn eine Modulversion noch nicht im Log verzeichnet ist, versucht die Checksum Database vor der Antwort, das betreffende Modul vom Origin-Server zu holen
  • Das Endpunktformat lautet $base/lookup/$module@$version
    • Zur $version von $module werden die Nummer des Log-Eintrags, go.sum-Zeilen und eine signierte Baumbeschreibung zurückgegeben
  • Bei der Abfrage einer Pseudo-Version von github.com/homebrew/homebrew-core werden ein Checksum-Datensatz und der Hash von go.mod zurückgegeben
  • Wenn ein Repository kein Versions-Tag hat, kommen die Regeln für Pseudo-Versionen von Go zur Anwendung

Experiment zur Registrierung eines neuen Go-Moduls

  • Nach dem Anlegen des neuen Go-Moduls github.com/gdbinit/fluxmatter wurde mit einer lookup-Anfrage geprüft, ob es registriert ist
  • Die Abfrage von @latest gab einen Fehler zurück, dass es sich nicht um eine kanonische Version handelt
    • bad request: version "latest" is not canonical
  • Die Abfrage von @v0.0.0 lieferte einen Fehler, dass diese Revision unbekannt sei
    • not found: ... invalid version: unknown revision v0.0.0
  • Nach erneuter Synchronisierung und Abfrage der Checksum Database war das Modul jedoch registriert
    • github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z
  • proxy.golang.org/github.com/gdbinit/fluxmatter/@latest lieferte die Pseudo-Version und Informationen zum GitHub-Origin, und die ZIP-Datei dieser Version ließ sich herunterladen und auf Komprimierung prüfen
  • Für das anfängliche Seeding musste keine exakte Version angegeben werden; schon eine Lookup-Abfrage, die einen Modulpfad und etwas versionsähnlich Aussehendes enthält, reicht aus

Auch Repositories ohne Go-Code werden in den öffentlichen Proxy geladen

  • Dasselbe Experiment gelang auch mit dem Repository github.com/gdbinit/readmem, das keinerlei Go-Code enthält
  • Die lookup-Anfrage meldete zwar, dass die Revision v0.0.0 unbekannt sei, doch in der Checksum Database wurde das Repository als Pseudo-Version registriert
    • github.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z
  • @latest bei proxy.golang.org gab die Pseudo-Version und Informationen zur Git-Origin des Repositories zurück
  • Die heruntergeladene ZIP-Datei enthielt keine Go-Dateien, sondern unter anderem Entitlements.plist, README, Xcode-Projektdateien und main.c
  • Das Experiment nutzte zwar ein GitHub-Repository, könnte aber grundsätzlich auch mit anderen Hosting-Seiten funktionieren, sofern das VCS unterstützt wird

GitHub-Abhängigkeit und Größenbeschränkungen

  • Die Zahl eindeutiger Pfade in der Checksum Database betrug 1.591.375, davon waren 1.515.957 Pfade github.com%
  • Rund 95 % der eindeutigen Pfade werden auf GitHub gehostet {p:95}
  • Diese Zahl ist eine rohe Statistik, bei der weder Forks noch Nicht-Go-Code-Projekte herausgefiltert wurden
  • Für Go-Modul-ZIPs gelten die Beschränkungen für Dateipfade und -größen
    • Modul-ZIP-Dateien: maximal 500 MiB
    • gesamte unkomprimierte Dateigröße ebenfalls maximal 500 MiB
    • go.mod-Datei: maximal 16 MiB
    • LICENSE-Datei ebenfalls maximal 16 MiB
  • Diese Limits sollen Denial-of-Service-Angriffe gegen Nutzer, Proxys und andere Teile des Modul-Ökosystems abmildern
  • Im Hinblick auf Missbrauchsmöglichkeiten sind 500 MiB dennoch groß genug

Mögliche Missbrauchsszenarien

  • Der öffentliche Go-Proxy könnte auf Entwicklerrechnern oder CI/CD-Servern genutzt werden, um Beschränkungen für Download-Ziele zu umgehen
    • vorausgesetzt, es gibt kein privates GOPROXY
    • Schadsoftware könnte Payloads in ein Repository hochladen und sie bei Bedarf über den Proxy abrufen
    • Selbst wenn die ursprüngliche Quelle verschwindet, könnte im Eintrag der Checksum Database nur eine kleine Spur verbleiben
  • Ein DoS gegen proxy.golang.org könnte schwer praktisch umzusetzen sein
    • Der Proxy kann dazu gebracht werden, beliebige Git-Repositories herunterzuladen
    • Ein denkbarer Angriff wäre, viele GitHub-URLs zu sammeln und dann massenhaft Anfragen an die lookup-API zu senden
    • Die Server-Implementierung ist unbekannt, aber es könnte Begrenzungen für parallele Verarbeitung geben, etwa über eine Work Queue
    • Auch Schutzmechanismen für GitHub-Bandbreite könnten greifen
    • Ebenso ist ein auf Speicherplatz zielender DoS denkbar, bleibt aber Spekulation
  • C2 (Command and Control) lässt sich leicht auf proxy.golang.org aufbauen
    • Über eine @latest-Abfrage kann die neueste Version eines bestimmten Moduls ermittelt werden
    • Die Payload kann eine einfache Datei sein oder in go.mod bzw. in Go-Quelldateien versteckt werden
    • Um die Nutzung eines einzelnen Repositories zu vermeiden, könnte eine Modul-DGA verwendet werden

C2-Download-Ablauf

Fazit und offene Fragen

  • Die Go-Checksum-Database und der Proxy können gemäß dem dokumentierten Verfahren nicht protokollierte Module vom Origin-Server holen und speichern
  • Der aktuelle Zustand wirkt nicht wie ein schwerwiegendes Problem der Go-Infrastruktur, lässt sich aber leicht missbrauchen und bietet Raum für Verbesserungen
  • Es könnte dokumentierte oder nicht öffentliche Gründe dafür geben, dass auch Nicht-Go-Repositories in Proxy und Checksum Database aufgenommen werden können
  • Um festzustellen, ob dies bereits aktiv missbraucht wird, müssten rund 1,6 Millionen eindeutige Repositories und nach aktuellem lokalen Datenbankstand etwa 22 Millionen Einträge untersucht werden
  • Warum einige legitime Nicht-Go-Projekte in der Database auftauchen, bleibt vorerst eine offene Frage

1 Kommentare

 
GN⁺ 2024-05-26
Hacker-News-Meinungen
  • Ein Onlinedienst, bei dem Nutzer Inhalte hochladen und diese Inhalte öffentlich sichtbar erscheinen, wird am Ende für Command-and-Control, Urheberrechtsverletzungen und CSAM-Hosting genutzt.
    Das gilt besonders für Dienste, die neben Datei-Hosting noch wichtige andere Zwecke erfüllen und sich deshalb schwer blockieren lassen; passiert ist das bereits bei Twitter[1], Telegram[2] und der PGP-Key-Infrastruktur[3] – von offensichtlichen Zielen wie GitHub ganz zu schweigen.
    [1] https://pentestlab.blog/2017/09/26/command-and-control-twitt...
    [2] https://www.blazeinfosec.com/post/leveraging-telegram-as-a-c...
    [3] https://torrentfreak.com/openpgp-keyservers-now-store-irremo...

    • Gmail, Google Groups, Google Drive und Gchat waren genauso betroffen; die gespeicherten Daten mussten nicht einmal öffentlich sein.
      Bei Gmail wurden Zugangsdaten verteilt, damit sich die Leute einloggen konnten, und dann lasen sie per IMAP die hochgeladenen Anhänge.
      Ich war früher Google SAD-SRE (Spam, Abuse, Delivery).
    • Auch PyPI scheint sich leicht für solche Zwecke nutzen zu lassen, weil man beliebige Nicht-Python-Dateien in Pakete legen kann.
      Es wäre auch möglich, Dateien als Base64 in Python-Code-Strings zu kodieren.
    • Ich weiß nicht, ob das schon passiert ist, aber ein weniger offensichtliches Ziel scheint HuggingFace zu sein.
  • Ich bin Googler, das ist meine persönliche Meinung, und ich kenne mich in diesem Bereich nicht gut aus.
    Ich hoffe, dass das Go-Team mit den GCP- und Drive-Teams zusammengearbeitet hat, denn Hosting bösartiger Dateien ist ein Problem, mit dem Google ständig zu tun hat.
    Es unterscheidet sich nicht wesentlich von anderen Endpunkten, über die Google bereits zulässt, dass Leute beliebige Daten einstellen.

    • Ich bin Ex-Googler und kenne das Go Dev Tools Team nicht gut, aber Google gehört unter den Großunternehmen, in denen ich gearbeitet habe oder über die ich von engen Freunden gehört habe, fast zu den besten, was solche interne Zusammenarbeit angeht.
      Google ist sehr gut darin, dass zentrale Teams Infrastruktur betreiben und unternehmensweit bereitstellen. Außer bei Messenger-Apps jedenfalls; reine Spekulation, aber ich vermute, dass das Go-Team einen internen Blob-Speicher nutzt und es auch ein internes Infrastrukturteam gibt, das Missbrauchsbekämpfung und Datei-Scanning automatisch übernimmt.
  • Auf PyPI gibt es auch ziemlich viele Nicht-Python-Projekte.
    Weil Python-Nutzer Library-Code unter Umständen nicht kompilieren können, braucht man die Möglichkeit, Wheels als kompilierte Binaries zu verteilen.
    Solcher Code ist oft in C geschrieben, aber Golang[1] geht ebenfalls; ich finde kein Beispiel, glaube aber, gesehen zu haben, dass es auch zur Verteilung von Anwendungen und nicht nur von Libraries genutzt wurde.
    Eine App in C zu schreiben, sie auf PyPI hochzuladen und den Nutzern zu sagen, sie sollen sie mit pip install installieren, ist ziemlich cool.
    [1] https://github.com/popatam/gopy_build_wheel_example

    • Selbst wenn man etwa eine Python-Anforderung einbaut, müsste jemand, der das böswillig erfüllen will, wohl nur minimalen Python-Stub-Code bereitstellen.
      Dann wäre es Linux, aber ls wäre in Python geschrieben; solche Spielchen sollte man wahrscheinlich lieber nicht spielen.
    • Bevor pip verlangt hat, dass man sich in einer Umgebung wie venv/virtualenv/pipenv/pyenv befindet, um ein Paket herunterzuladen, wäre dieser Anwendungsfall wohl deutlich nützlicher gewesen.
    • Es gibt auch pip install cmake, und proprietäre Binaries gehen ebenfalls, etwa mit pip install nvidia-cudnn-cu12.
    • In letzter Zeit nutze ich PyPI viel für Nicht-Python-Tools wie FFmpeg und Eigen.
      Das ist einer der Gründe, warum ich Homebrew komplett aufgeben konnte.
  • Vielleicht ist das naiv, aber ich verstehe nicht, wie sich das vom Hochladen einer Datei in ein GitHub-Repository unterscheidet.
    Besteht der Unterschied nur darin, dass man bei GitHub ein Konto erstellen muss? Auch auf GitHub kann man beliebige Daten speichern, und es gibt kein 500-MB-Limit.

    • GitHub hat für anonyme Requests ziemlich strenge Request-Limits.
  • Das Modulsystem von CUE wird endlich veröffentlicht, und MVS ähnelt Go, ist aber auf OCI-Infrastruktur aufgebaut.
    Falls man sich für Dependency-Management-Systeme interessiert, sind hier ein paar Links.
    proposal: https://github.com/cue-lang/proposal/tree/main/designs/modul...
    custom registry: https://cuelang.org/docs/tutorial/working-with-a-custom-modu...
    road map: https://github.com/orgs/cue-lang/projects/10/views/8
    Seit 0.9.0-alpha-5 sind Module standardmäßig aktiviert: https://github.com/cue-lang/cue/releases/tag/v0.9.0-alpha.5
    Bei Go Sum stützt das Trillian-Projekt das Transparenz-Log: https://github.com/google/trillian
    CUE plant, auf OCI-Optionen wie Attestations aufzusetzen.

    • Ich verstehe nicht, was das mit dem verlinkten Artikel zu tun hat.
  • Ich habe mit der Idee herumgespielt, mich an den golang proxy und sumdb dranzuhängen – genauer: sie zu missbrauchen –, um ein kostenloses Transparenz-Log für Checksummen beliebiger URLs zu erstellen
    https://getsum.pub/

  • Vielleicht bin ich ja begriffsstutzig, aber ich verstehe nicht, was hier genau das Problem sein soll
    Dass der Proxy Nicht-Go-Repositories cached, mag etwas verschwenderisch sein, aber selbst wenn er das nicht täte, könnte man ihn doch ohnehin dazu bringen, beliebige Daten zu speichern, indem man ihn Go-Repositories cachen lässt?
    Wenn ich nicht irgendetwas übersehe, klingt das nach einer völligen Nicht-Nachricht

    • Ich glaube nicht, dass du etwas übersehen hast
      Die Neuigkeit hier scheint nur zu sein, dass ein öffentlicher Proxy ohne Sicherheitsmechanismen entgegennimmt, was er proxen soll, und es dann auf unsichere Weise öffentlich bereitstellt
      Im Artikel heißt es, manche überwachten Netzwerke könnten golang-proxy-URLs mehr vertrauen als beliebigen Web-URLs, wodurch sich das etwa zur Umgehung von Reputationsfiltern nutzen ließe; solche Methoden gibt es aber bereits in vielen Varianten, und diese hier wirkt nicht besonders speziell
  • Off-topic, aber der Domainname verhieß wegen des Wortspiels nichts Gutes, also habe ich put.as geprüft, und es war im Großen und Ganzen das, was ich erwartet hatte

    • https://put.as/ ist etwas NSFW
    • Ich habe den Fehler gemacht, das am Arbeitsplatz zu öffnen
    • Es ist zwar ein spanisches Wort im Plural. Aber …
  • Das ist ein bereits bekanntes Issue: https://github.com/golang/go/issues/31866

    • Dieser Fix würde zwar helfen, Versehen zu vermeiden, aber jemand, der es absichtlich tun will, müsste doch nur eine .mod- und eine .go-Datei im Root hinzufügen, oder?
    • Dass Marwan in diesem Issue auftaucht, überrascht mich überhaupt nicht
      Er und Aaron haben Athens gebaut, und soweit ich weiß, hat Marwan die erste Implementierung des Go-Download-Protokolls geschrieben, auf der Athens basiert
      Interessant an diesem Issue ist, dass Athens bereits den erwähnten Befehl go mod download -json als Vorabprüfung für die Modulvalidierung verwendet
      Im Großen und Ganzen liefert Athens ein Repository aus, wenn es als Modul durch die Go-Modulbefehle kommt
      Etwas formaler gesagt: Man muss Modulversionen, Pseudoversionen und +incompatible erzeugen können, und dieses Modul samt Abhängigkeiten muss gültige Checksummen ergeben
      Modul-Checksummen haben derzeit damit zu tun, die .mod-Datei, alle Dateien sowie rekursiv jede Abhängigkeit einzubeziehen
      Wie der Autor sagt, reicht deshalb schon ein grundlegendes Go-Programm, um konstruktionsbedingt viel Platz für beliebige Dateien zu haben
  • Das W3C hat die Grundlage dafür gelegt, dass alles im Web stark cachebar sein kann; daher ist es seltsam, dass es so wenige universelle Proxy-Caches gibt
    Senden Publisher unnötigerweise kurze Cache-Control: max-age- oder Vary: Cookie-Antworten?
    Zahlen ISPs zu viel für Transit im Vergleich zu Peering?

    • Im Allgemeinen gibt es keine Möglichkeit sicherzustellen, dass ein Cache den Inhalt nicht verändert hat
      Bei Nicht-HTTPS-Sites könnte ein ISP-Proxy zum Beispiel Werbung einfügen
      Software-Downloads haben normalerweise Signaturen und Checksummen, aber bei beliebigen Inhalten gibt es so etwas kaum