Ein Token von 2023, gefunden in 25 Minuten

Ein autonomer Pentest-Agent zieht bei Baseten ein Admin-Token aus der Build-Historie eines alten Images. Der Fehler steckt in vielen Dockerfiles.

7 Min. Lesezeit

Das Sicherheitsunternehmen Strix wollte Baseten als Inferenz-Anbieter nutzen. Vorher, so beschreiben sie es in ihrem Bericht vom 15.09.2026, haben sie den eigenen autonomen Pentest-Agenten auf *.baseten.co losgelassen. Ohne Zugangsdaten, ohne Quellcode, ohne Hinweis worauf er achten soll.

Nach rund 25 Minuten hatte der Agent ein gültiges GitHub-Token mit Admin-Rechten auf internen Repositories von Baseten. Gefunden hatte er es in einem Container-Image, das am 03.03.2023 gebaut worden war. Im Juli 2026 funktionierte es immer noch.

Baseten ist keine kleine Bude. Die Firma ist mit 13 Milliarden Dollar bewertet, viele ernsthafte Unternehmen hängen an ihrer Infrastruktur, und laut Strix setzt das Sicherheitsteam selbst schon KI-Werkzeuge ein. Es hat trotzdem über drei Jahre niemand gemerkt.

Der Weg dorthin

Die Kette ist unspektakulär, und das ist der Punkt.

Interessant ist, wo das Token lag. Nicht in einer Datei im Dateisystem des Images, sondern in dessen Konfiguration, im Feld history[].created_by. Da steht, wie ein Build-Schritt zustande kam, und in diesem Fall stand dort ein RUN-Befehl mit dem ausgeschriebenen Wert von GITHUB_TOKEN.

Beim Pull lädt man diese Konfiguration mit herunter. Das ist der Teil, der weh tut. Wer die Datei mit den Zugangsdaten sauber aus dem Layer löscht, aber die Build-Historie stehen lässt, hat nichts gelöscht.

Der Fehler im Dockerfile

Das Muster dahinter kennt fast jeder, der schon mal private Abhängigkeiten in einem Build gebraucht hat:

ARG GITHUB_TOKEN
RUN GITHUB_TOKEN=${GITHUB_TOKEN} bash -c '\
  if [[ "${GITHUB_TOKEN}" != "" ]]; then \
    git config --global --add \
      url."https://${GITHUB_TOKEN}@github.com/".insteadOf "git@github.com:"; \
  fi'

Man braucht ein privates Repository, reicht ein Token als Build-Argument rein, Git authentifiziert sich, der Build läuft. Docker warnt ausdrücklich davor, dass Build-Argumente in den Metadaten landen können.

Zwei Probleme stecken darin. Erstens das Token in der Historie. Zweitens schreibt git config --global die authentifizierte URL in die Git-Konfiguration, die dann im Image liegt. Wer nur das erste behebt, hat immer noch das zweite.

Die Gegenmaßnahmen sind unromantisch:

ProblemLösung
Token als ARG im BuildBuildKit Secret Mount, das Secret landet nicht im Image
Zugangsdaten in git config --globaltemporäre Authentifizierung, die nach dem Schritt verschwindet
Alte Images schon veröffentlichtToken widerrufen, Dockerfile ändern reicht nicht
Scanner prüft nur DateienLayer und Image-Config prüfen

Darin steckt die eigentliche Lehre aus dem Fall. Mit TruffleHog hat Strix die Layer durchsucht, die AWS-Keys gefunden, getestet, verworfen, und dann in der Config weitergesucht. Ein Scan, der beim Dateisystem aufhört, hätte das Token nie gesehen, egal wie gründlich er die Layer durchgeht und wie aktuell seine Signaturen sind.

Was der Agent daran ändert

Keine der einzelnen Aktionen ist neu. Subdomains aufzählen, eine offene Registry finden, ein Image ziehen, Secrets suchen, Rechte prüfen: Das ist Standardarbeit. Neu ist, dass sie am Stück passiert ist, ohne Zwischenfragen, ohne dass jemand die nächste Aktion hätte freigeben müssen, und in einer halben Stunde.

Besonders die Stelle, an der ein billiger Scanner aufgegeben hätte. Zuerst fand der Agent AWS-Keys, prüfte sie mit einem lesenden sts:GetCallerIdentity, bekam InvalidClientTokenId zurück, und suchte dann weiter, statt einen Fehlalarm zu melden. Genau diese Beharrlichkeit unterscheidet Fundlisten von Funden.

Wir haben die ökonomische Seite davon in Bugonomics beschrieben: Finden wird gerade sehr billig, Beheben nicht. Dieser Fall ist ein Lehrbuchbeispiel. 25 Minuten Agentenlaufzeit gegen ein Token, das drei Jahre lang Admin war.

Wer mit einem eigenen Agenten die eigene Infrastruktur prüfen will: Strix ist Open Source. Wer ihn auf fremde Systeme richtet, braucht eine Erlaubnis, sonst ist es kein Pentest.

Wie Baseten reagiert hat

Erfreulich ist der Teil danach, und Strix sagt das selbst deutlich.

ZeitpunktWas passierte
13.07.2026, 23:10 UhrStrix meldet Token, öffentliches Harbor-Projekt und Rechte
14.07.2026, vormittagsBaseten stellt das Harbor-Projekt auf privat, Strix weist darauf hin, dass das Token noch lebt
14.07.2026, 16:34 UhrBaseten bestätigt als kritisch, Token rotiert, bittet um Löschung der geladenen Images
14.07.2026, 17:05 UhrStrix bestätigt die Löschung und meldet zwei weitere Funde geringerer Schwere
17.07.2026Baseten schließt die restlichen Funde
September 2026Strix kündigt die Veröffentlichung an und schickt den Entwurf vorab

Vom Report bis zum rotierten Token: gut 17 Stunden. Dass Strix beim ersten Fix nachgehakt hat, weil das Token trotz privat gestellter Registry weiterlief, ist dabei der Schritt, den man leicht übersieht, und der Grund, warum eine Meldung nicht mit dem ersten "ist behoben" des Anbieters endet. Eine Registry zu schließen holt keine Kopie zurück, die schon jemand heruntergeladen hat.

Die eine Sache für morgen früh

Nehmt ein Image, das ihr veröffentlicht, und schaut in seine Historie:

docker history --no-trunc IMAGE

Oder direkt in die Konfiguration, wenn das Image bereits lokal liegt:

docker inspect IMAGE --format '{{json .Config}}'

Wenn dort ein Token, ein Passwort oder eine authentifizierte URL steht, ist der erste Schritt nicht das Aufräumen des Dockerfiles, sondern das Widerrufen der Zugangsdaten, weil jede bereits heruntergeladene Kopie des Images sonst weiter gültig bleibt. Alles andere kommt danach. Wie man solche Fragen dauerhaft in den Alltag bekommt, steht bei uns unter KI-Tools sicher nutzen.

Quellen4