Chris Rohlf
Im vergangenen Jahr haben wir veröffentlicht viele Daten, die zeigen, dass KI-Frontiermodelle mehr als fähig sind Entdecken und ausnutzen Sicherheitslücken. Zu zeigen, wie bisher unbekannte Sicherheitslücken ausgenutzt werden könnten, ist jedoch nur der Anfang dessen, was KI-Frontier-Modelle für die Cyberverteidigung leisten können. In diesem Beitrag fassen wir einige unserer ersten Forschungsarbeiten zur automatisierten Analyse und Reduzierung der Angriffsfläche zusammen: den Prozess, bei dem KI-Modelle den erreichbaren Code kartieren und dann der Zugriff auf alles entfernt oder eingeschränkt wird, was eine bestimmte Bereitstellung nicht freigeben muss.
Automatisierte Analyse und Reduzierung der Angriffsfläche kann die Sicherheit eingesetzter Software kurzfristig deutlich verbessern. Software allein durch das Finden und Beheben einzelner Schwachstellen abzusichern, skaliert schlecht: Die Behebung selbst einer kritischen Schwachstelle sagt nichts über die verbleibenden Schwachstellen aus. Im Gegensatz dazu kann die Verringerung der Code-Erreichbarkeit den Schweregrad unbekannter Schwachstellen verringern, einschließlich derjenigen, die möglicherweise aktiv ausgenutzt werden. Diese Techniken entfernen die unbekannten Schwachstellen im Code nicht, aber sie machen die Schwachstellen schwerer erreichbar und verringern dadurch ihren Schweregrad. Die Minimierung der Angriffsfläche von Codebasen führt zu besserer Sicherheit, ohne jemals eine einzelne Schwachstelle finden oder beheben zu müssen.
Im Folgenden geben wir einen Vorausblick auf einige der Daten, die wir durch die Anwendung von Techniken zur Reduzierung der Angriffsfläche auf große Open-Source-Codebasen mit Claude gesammelt haben. Unser Ziel dabei ist es zu zeigen, dass Claude die Softwaresicherheit durch die von uns beschriebenen Methoden auf sinnvolle und wirtschaftliche Weise verbessern kann. Konkret haben wir Claude über 25 Projekte (in verschiedenen Sprachen) laufen lassen, und es entdeckte 493 Probleme. Vor dem Hintergrund dieser Ergebnisse diskutieren wir, wie Entwickler ähnliche Techniken auf ihren eigenen Code anwenden können.
Methoden zur Verkleinerung der Angriffsfläche von Codebasen
Mit der Zeit neigt Software dazu, komplizierter zu werden, wobei sich neue Abstraktionsschichten, neue Abhängigkeiten und neue Funktionen langsam übereinander türmen. Dies kann oft dazu führen, dass sie sich sehr anders verhält, als ihre ursprünglichen Entwickler es beabsichtigt hatten. Und das ist ein wesentlicher Grund, warum Angreifer oft so erfolgreich sind: Sie untersuchen genau, wie sich ein Stück Code in der Praxis verhält, und nicht, wie seine Entwickler beabsichtigt oder geplant hatten, dass es funktioniert.
Automatisierte Angriffsflächenanalyse und -reduzierungstechniken zielen darauf ab, unnötige Angriffsflächen zu entdecken und den entsprechenden Code dann hinter ein geeignetes Tor zu verschieben. Dies kann bedeuten, Code von der Vor- zur Nachauthentifizierung zu verlagern, von 0-click zu 1-click, von standardmäßig aktiv zu ausdrücklichem Opt-in oder von der ersten Nachricht eines Protokolls zu einem tieferen Punkt im Zustandsautomaten der Sitzung. Ein Teil des Codes soll vor diesen Phasen erreichbar sein, aber vieles davon nicht, und angesichts der Komplexität, die mit Codeänderungen über Tausende oder Millionen von Codezeilen hinweg einhergeht, bleibt eine unnötige Offenlegung von Funktionen oft unbemerkt.
Es gibt zahlreiche Möglichkeiten, die Angriffsfläche von Code zu reduzieren. In unseren eigenen Forschungsarbeiten konzentrierten wir uns insbesondere auf drei Methoden:
- Standardkonfiguration. Wir haben Code identifiziert, der in der ausgelieferten Standardkonfiguration erreichbar war, aber nicht für die Nutzung durch eine Standardfunktion vorgesehen war.
- Konfigurationswechselwirkung. Wir fanden Paare von Optionen, die für sich betrachtet sinnvoll aussahen, aber zusammen konfiguriert Probleme verursachten (beispielsweise, indem stillschweigend eine Sicherheitskontrolle deaktiviert wurde oder das Verhalten einer Funktion undefiniert blieb, während ihr Code weiterhin erreichbar bleibt).
- Frühe Erreichbarkeit. Wir identifizierten sicherheitssensitiven und komplexen Code, der entweder vor einer geeigneten Sicherheitskontrolle (z. B. Authentifizierung) ohne Interaktion des Opfers oder vor einem Protokollzustandsübergang ausgeführt wird.
Wir wollten verstehen, wie gut Claude diese drei Beschreibungen von Angriffsflächen nutzen konnte, um echte Probleme in großen und komplexen Codebasen zu entdecken. Wir analysierten 25 Open-Source-Projekte (mit über fünf Millionen Codezeilen), die in Sprachen wie C, C++, Java, PHP und Python geschrieben waren. Claude erstellte ein Inventar von über 17,000 Konfigurationsoptionen über diese Projekte hinweg. Davon bezogen sich 3,842 auf die Konfiguration einer sicherheitsrelevanten Steuerung oder Funktion.
Für unsere Forschung haben wir diese Open-Source-Projekte in drei große Kategorien unterteilt:
- Öffentlich„ Diese Projekte sind so gebaut, dass sie jedem Nutzer dienen, der sie erreichen kann, oder dass sie Daten verarbeiten, die von Parteien stammen, die sie nicht identifizieren können. (Beispiele hierfür sind ein DNS-Resolver, der Antworten von Upstream-Servern verarbeitet, oder ein Proxy, der Antworten von Origin-Servern verarbeitet.)“
- Gated. Diese Projekte sind dafür gedacht, authentifizierte Benutzer zu bedienen (z. B. Benutzer mit einem Passwort, Schlüssel oder Zertifikat oder registrierte Geräte). Die Software erlaubt weiterhin beliebige Client-Anfragen, aber es ist keine Funktionalität verfügbar, bis der Benutzer authentifiziert wurde.
- Privat. Diese werden erstellt und bereitgestellt, um die private Umgebung des Betreibers selbst zu bedienen. In diesen Fällen ist das Hinzufügen einer Steuerung wie einer Authentifizierungsschicht optional, liegt aber außerhalb des Geltungsbereichs der Software.
Diese Kategorien helfen dabei, unsere Ergebnisse einzuordnen. Beispielsweise würden wir erwarten, dass ein erheblicher Prozentsatz einer Public-Codebasis in der Standardkonfiguration erreichbar ist (da sie darauf ausgelegt ist, beliebigen Nutzern zu dienen), während das Gegenteil für Projekte in der Kategorie Gated gilt.
Claudes Ergebnisse
Claude fand 493 Probleme mit den drei oben beschriebenen Methoden. Mehrere Themen fielen uns bei seinen Ergebnissen besonders auf.
Erstens stellte Claude fest, dass alle elf Gated-Projekte Code enthielten, der unbeabsichtigt erreichbar war (obwohl dieser Code bei keinem dieser Projekte einen erheblichen Prozentsatz der gesamten Codezeilen ausmachte). Konkret entdeckte Claude 93 unnötige Pre-Authentication-Oberflächen in der Standardkonfiguration der Gated-Kohorte, 37 Stellen, an denen Code ausgeführt oder Zustände aufgezeichnet wurden, bevor eine Authentifizierung stattfand, und 61 Konfigurationskombinationen, die eine vom Administrator konfigurierte Pre-Authentication-Kontrollmaßnahme stillschweigend abschwächten. Keines dieser Probleme wäre für sich genommen sofort ausnutzbar gewesen, aber sie schaffen eine viel größere Angriffsfläche als notwendig.
Zweitens ergab Claudes Arbeit zur Minderung von Konfigurationsinteraktionsproblemen einige der interessantesten und wirkungsvollsten Erkenntnisse (über alle Projekttypen hinweg). Claude fand 162 sicherheitsrelevante Konfigurationskombinationen, die sowohl ohne Fehler oder Warnung akzeptiert wurden als auch ohne jegliche Dokumentation des erwarteten Verhaltens ihrer Interaktion. In 94 dieser Fälle schaltete die Kombination stillschweigend eine Sicherheitskontrolle aus. Die anderen 68 Fälle waren Inkonsistenzen in denselben Sicherheitsmechanismen (z. B. blieb ein Listener oder Login-Pfad erreichbar, obwohl die Konfiguration besagte, dass er deaktiviert war; zwei Einstellungen konfligierten, und welche davon Vorrang hatte, hing von ihrer Reihenfolge in der Konfigurationsdatei ab, oder ein vom Administrator konfigurierter Schutz tat nichts, während sein Code weiterhin erreichbar blieb). Während einige dieser Konfigurationskombinationen möglicherweise nicht standardmäßig oder selten verwendet sind, würde nichts einen Administrator dieser Projekte daran hindern, den Code auf diese Weise zu konfigurieren und bereitzustellen.
Claude fand auch ein bestimmtes Muster, das unabhängig voneinander in vier nicht verwandten Projekten auftauchte. Es funktionierte wie folgt: Ein Administrator würde eine Zugriffsregel für entfernte Clients unter Verwendung eines Hostnamens oder eines Wildcard-Musters schreiben, anstelle einer IP-Adresse. Wenn ein Client sich verbindet, würde der Server diese Regel mit dem Hostnamen des Clients vergleichen. Ob der Server diesen Hostnamen überhaupt erfuhr, hing von einer separaten Option ab, die Reverse-DNS-Lookups steuerte (die in den meisten Projekten deaktiviert war). Bei deaktivierten Lookups hatte der Server nur die IP-Adresse des Clients. Der Server würde die Regel mit dieser Adresse als Klartext vergleichen. Aber ein Hostnamen-Muster kann nie mit einer IP-Adresse übereinstimmen. Das bedeutet, dass eine Deny-Regel nie greifen würde und die Liste im Fehlerfall offen bleiben würde (fail open). Einige der betroffenen Projekte erwähnen in ihrer Dokumentation, dass Hostnamen-Regeln davon abhängen, dass DNS-Lookups aktiviert sind, aber keines gibt an, dass eine auf diese Weise geschriebene Deny-Liste ein Fail-open-Verhalten hätte.
Drittens stellte Claude häufig fest, dass zwei Konfigurationsoptionen auf eine Weise miteinander kollidierten, die eine Sicherheitsprüfung stillschweigend fallen ließ. In einem Fall konnte ein Administrator verlangen, dass sich jeder verbindende Client mit einem Zertifikat ausweist. In einem anderen war der Code nur dafür gedacht, diese Prüfung aufzuschieben, bis sie erforderlich war, deaktivierte sie aber in Wirklichkeit vollständig. Ein Server, der so konfiguriert war, dass er nur vertrauenswürdige Clients akzeptierte, akzeptierte daher eine Verbindung von jedem. Dadurch wurde eine große Menge an Code zur Anforderungsverarbeitung (der eigentlich hinter der Zertifikatsprüfung liegen sollte) für nicht authentifizierte Clients zugänglich. Die Software akzeptierte beide Konfigurationsoptionen zusammen ohne Warnung unter den Standardeinstellungen; die Dokumentation erwähnte nie, dass die Zertifikatsanforderung entfallen war. Dieser widersprüchliche Satz von Konfigurationsoptionen war seit 21 Jahren in der Software vorhanden.
Schließlich entdeckte Claude ein Problem in einem Projekt, das die Authentifizierung standardmäßig deaktiviert ließ (und erwartete, dass der Administrator sie einschaltet). Die Option, die TLS-Client-Zertifikate verlangte, wurde auf den meisten Netzwerk-Endpunkten des Projekts beachtet, auf einigen wenigen anderen jedoch stillschweigend ignoriert (solche, die ihre TLS-Einstellungen über einen Pfad mit einem anderen Namespace luden). Das bedeutete, dass ein Client ohne Zertifikat den TLS-Handshake abschließen und eine Sitzung auf einem betroffenen Endpunkt öffnen konnte. Über die ignorierte Einstellung würde nichts protokolliert. Ob die Option wirksam wird, hing vom Namespace ab, den der Administrator in der Konfiguration verwendete. Eine Form des Einstellungsnamens funktioniert auf jedem Endpunkt, aber die kürzere Form – die in den meisten Beispielen verwendet wird – wird auf den betroffenen Endpunkten verworfen.
Fallstudie: OpenSSH
Überraschenderweise war das Projekt, bei dem Claude die größte Verringerung der Angriffsfläche erreichte, OpenSSH – gemessen als Prozentsatz der gesamten Codezeilen. OpenSSH ist eine hochwertige C-Codebasis, die sicheren Fernzugriff bereitstellt. Claude verfolgte die Eingabepfade des SSH-Daemons in der Version 10.4 und fand etwa 4.500 Zeilen neuen und experimentellen Code für ML-DSA-44, einen Post-Quanten-Signaturalgorithmus, den ein nicht authentifizierter Client in der Standardkonfiguration erreichen konnte. ML-DSA ist nicht standardmäßig aktiviert, aber der Code war dennoch erreichbar, selbst wenn ein Administrator den Algorithmus explizit ausgeschlossen hatte. Der Daemon parsepte den Schlüsseltyp, den der Client sendete (und bei Zertifikaten verifizierte er auch die eingebettete CA-Signatur). Erst danach prüfte er, ob dieser Schlüsseltyp erlaubt war. Es gibt keinen guten Grund, warum dieser komplexe, speicherunsichere Code erreichbar sein sollte, wenn er nicht durch die Konfiguration aktiviert wurde. Diese 4.500 Zeilen Code in ML-DSA-44 machen etwa 16 % des eigenen Pre-Authentifizierungs-Codes von OpenSSH in einem Standard-sshd aus. Wir meldeten dies den OpenSSH-Entwicklern zusammen mit Patches. Die Version 10.5 verschob die Prüfung vor das Parsen.
Claude bemerkte dann zwei Lücken in der 10.5-Prüfung. Die erste war eine Diskrepanz zwischen dem, was der Client behauptete, und dem, was er sendete. Die Prüfung betrachtete den vom Client beworbenen Algorithmusnamen, aber der Parser wurde durch eine Typzeichenkette gewählt, die im Schlüssel-Blob eingebettet war. Ein Client konnte ssh-ed25519 bewirben, die Prüfung bestehen und dennoch einen ML-DSA-Schlüssel senden, der geparst würde.
Die zweite Lücke betraf die Zertifikate der 10.5-Prüfung. Zertifikatsschlüsseltypen waren standardmäßig aktiviert. Bei einem von einem Angreifer gelieferten Zertifikat würde sshd den eingebetteten öffentlichen CA-Schlüssel deserialisieren und eine vollständige Signaturprüfung damit durchführen, für jeden unterstützten Algorithmus, bevor die Konfiguration konsultiert wurde. Der Pfad der Benutzerschlüsselsignatur war ordnungsgemäß hinter der Liste der öffentlichen Schlüssel gesperrt, die sich mit dem Server verbinden dürfen, aber der CA-Signaturpfad war es nicht. Das machte jeden öffentlichen Schlüsselsignaturprüfer in der Binärdatei für jeden Client erreichbar, der einen Schlüsselaustausch abschließen konnte. Wir erstellten einen Proof of Concept und erarbeiteten mehrere Iterationen einer Korrektur mit den Maintainern. Die daraus resultierende Änderung platzierte die Prüfungen im Parser selbst. Der kodierte Schlüsseltyp und der CA-Signaturalgorithmus des Zertifikats werden jetzt mit den konfigurierten Zulassungslisten verglichen bevor die entsprechende Deserialisierung oder Signaturprüfung erfolgt.
Diese Korrektur entfernte etwa ein Fünftel des OpenSSH-Codes, den ein nicht authentifizierter Client zuvor in einem Standard-Build erreichen konnte. Fast alles davon war der neue ML-DSA-Code, der nun unerreichbar ist, selbst für einen Client, der über seinen Schlüsseltyp lügt oder einen ML-DSA-CA-Schlüssel in einem ansonsten akzeptablen Zertifikat versteckt. Zertifikate, die mit einem erlaubten CA-Algorithmus signiert sind, werden weiterhin geparst und verifiziert, aber die Prüfung läuft nun vor dem Parsen. Die Maintainer haben die hier beschriebenen Änderungen in Version 10.6.
OpenSSH ist einer der am intensivsten auditierten Codes der Welt. Wenn unnötige Erreichbarkeit dort überlebt, kann sie überall überleben. Das deutet darauf hin, dass die Verringerung der Angriffsfläche von Codebasen wahrscheinlich in vielen wichtigen Codebasen Dividenden abwirft.
Fazit
Es ist vielleicht unmöglich zu messen, wie viele unbekannte Schwachstellen sich in einer beliebigen Codebasis befinden. Und es gibt reichlich Daten, die zeigen, dass Angreifer diese Schwachstellen mit genügend Zeit finden können und werden (insbesondere wenn sie mit Frontier-KI-Modellen ausgestattet sind). Aber ein Verteidiger, der unnötige Angriffsfläche in seinem Code verringert, reduziert das Risiko aller unbekannten Schwachstellen in diesem Code auf einmal – einschließlich aller, die bereits still ausgenutzt werden. OpenSSH ist einer der am meisten auditierten C-Codes der Welt. Dennoch enthielt er etwa 4.500 Zeilen experimentellen Signatur-Parsing-Codes, die ein Angreifer vor jeder Konfigurationsprüfung hätte erreichen können.
Wir haben die oben beschriebenen Techniken zur Verringerung der Angriffsfläche in eine wiederverwendbare Claude-Skill kombiniert, an deren Integration wir arbeiten in OSS-Scanner. Die Skill führt drei Durchläufe über eine Codebasis aus. Jedes Mal startet sie neu (mit einem leeren Kontextfenster), außer für ein kurzes Stück Kontext, das sie vom vorherigen Durchlauf erhält.
Im ersten Durchlauf prüft die Skill, ob es Code gibt, den die Standardkonfiguration erreichbar lässt, der aber von keiner beabsichtigten Funktionalität genutzt wird. Im zweiten Durchlauf betrachtet sie den Code, der vor harten Schranken (wie Authentifizierung) oder vor Protokoll-Zustandsautomaten-Einstiegspunkten läuft, in jeder Konfiguration. Schließlich bewertet die Skill im dritten Durchlauf, welche Kombinationen von Konfigurationen eine Sicherheitskontrolle still abschalten oder ihre Wirksamkeit mindern. In jedem Durchlauf erstellt Claude ein Inventar der Einstiegspunkte aus dem Code und bestätigt jeden Befund mit einer dynamischen Ablaufverfolgung des Programms. Für Befunde der Standardkonfiguration entwirft und implementiert sie eine Schranke und stellt sicher, dass dies keine Änderung des Standardverhaltens des Programms bewirkt. Dies tut sie, indem sie die eigene Testsuite des Projekts (zusammen mit einer Black-Box-Regressionssuite) gegen den Baseline ausführt; sie verlangt, dass modifizierte Builds identische Ergebnisse liefern. Jede Schranke, die eine Nicht-Standardkonfiguration ändern würde, wird einem Menschen zur Entscheidung vorgelegt. Für die anderen beiden Durchläufe schlägt sie Änderungen vor wie das Umordnen einer Konfigurationsprüfung, das Hinzufügen einer Bedingung zur Durchsetzung der Zustandsreihenfolge oder das Zurückweisen der Kombination beim Laden der Konfiguration. Auch ohne die Verwendung dieser speziellen Skill halten wir die hier skizzierte breite Methodik für wahrscheinlich wirksam.
Die in diesem Beitrag beschriebenen Techniken bieten nur einen sehr begrenzten Einblick in die Möglichkeiten zur Verringerung der Angriffsfläche. So ist Claude beispielsweise auch in der Lage, Laufzeitprotokolle mit Konfigurationen zu analysieren und zu vergleichen, um sicherzustellen, dass nur die erforderlichen Funktionen aktiviert wurden: Das Deaktivieren ungenutzter Parser, Legacy-Protokolle und Dashboards reduziert das Sicherheitsrisiko mit geringem Aufwand.
Wir ermutigen Organisationen, Claude zu nutzen, um die Angriffsfläche der Software, von der sie abhängen, zu analysieren und zu verkleinern. Wir erwarten, dass dies eine der praktischsten Möglichkeiten ist, mit der Entwickler die Sicherheit des Codes, auf dem die Welt bereits läuft, messbar verbessern können.