Switch 2 Jailbreak: Exploit-Kette gegen Secure Boot
Der Switch 2 Jailbreak verkettete einen BASIC-Interpreter-Overflow, einen fs-Pfadfehler und eine Loader-Race. Was er über Secure Boot und Exploit-Ketten lehrt.

Eine frisch ausgelieferte Konsole kommt mit einem gehärteten Microkernel, Pointer Authentication bei jeder Rücksprungadresse, Execute-Only-Speicher, Control-Flow Integrity und einer signierten Boot-Kette. Zwei Wochen später führt ein Hobbyist beliebigen nativen Code als Dateisystem-Dienst aus, also im vertrauenswürdigsten Prozess des Geräts. Das ist die Geschichte des ersten öffentlichen Switch-2-Jailbreaks, und sie sollte jeden Ingenieur, der sichere Systeme baut, ein wenig nervös machen. Nicht weil die Schutzmechanismen versagt hätten, die meisten funktionierten genau wie geplant, sondern weil der Exploit sie nie frontal knacken musste. Er umging sie, Logikfehler für Logikfehler.
Ich verbringe seit Jahren viel Zeit auf der defensiven Seite der Infrastruktursicherheit, und das Muster hier begegnet mir in Incident-Reviews aus dem Produktivbetrieb öfter, als mir lieb ist: eine Kette einzeln unspektakulärer Bugs, von denen jeder gerade so viele Rechte verschafft, dass man den nächsten erreicht. Der initiale Einstieg lag nicht einmal in der Switch 2 selbst. Es war ein Speicherkorruptionsfehler in einem Switch-1-Spiel, im SmileBASIC-Interpreter, der im Kompatibilitätsmodus lief. Von dort kletterte die Kette über den Dateisystem-Dienst, nutzte eine Race Condition im Programm-Loader aus und endete mit der Möglichkeit, nativen Code unter der Identität eines beliebigen Sysmoduls auszuführen. Jedes Glied verdient einen genauen Blick, denn jedes entspricht einem Fehler, den ich auch bei serverseitigen Systemen Teams habe machen sehen.
Warum die Browser-Tür verschlossen war und die Spiele-Tür nicht
WebKit ist das klassische erste Ziel bei Konsolen-Jailbreaks, und der Forscher bekam dort tatsächlich über ein Captive Portal und einen n-Day einen Fuß in die Tür. Doch dann war schnell Schluss. Der Browser-Stack der Switch 2 ist von drei Schutzmechanismen umgeben, die sich brutal gut ergänzen: Execute-Only-Speicher (XOM) bedeutet, dass man selbst mit beliebigem Lese- und Schreibzugriff keine nützlichen Code-Gadgets finden kann; Pointer Authentication (PAC) bedeutet, dass man einen Code-Pointer ohne ein Signaturorakel nicht fälschen kann; und CFI bedeutet, dass selbst ein gültig signierter Pointer an den meisten Aufrufstellen die Typprüfungen nicht besteht. Einen dieser Mechanismen zu brechen ist ein Forschungsprojekt. Alle drei verkettet zu brechen, aus einer JavaScript-Sandbox heraus, ist eine Lebensaufgabe.
Also wich der Angriff auf den schwächsten Mandanten der Maschine aus: Drittanbieter-Spiele für die Switch 1. Die Überlegung war nüchtern und korrekt. Spielebinaries werden von Studios gebaut, nicht vom Sicherheitsteam von Nintendo, und daher ist es weit weniger wahrscheinlich, dass sie mit dem vollen Schutz-Stack kompiliert wurden. Die Switch 1 hatte nie PAC, also erbt alles, was in ihrer Kompatibilitätsumgebung läuft, diese Schwäche. Noch besser: Die Root of Trust der Switch 1 wurde vor Jahren kompromittiert, was bedeutet, dass ihr Spielecode offline entschlüsselt und geprüft werden kann. SmileBASIC, ein riesiger C++-BASIC-Interpreter, der auf dem 3DS bereits ausgenutzt worden war, war das naheliegende weiche Ziel.
Hier steckt eine Lehre, die über Konsolen hinausreicht: Deine Sicherheitslage ist die Sicherheitslage des am wenigsten gehärteten Codes, den du auszuführen bereit bist. Abwärtskompatibilität ist eine Angriffsfläche, die du erbst, nicht eine, die du wählst. Das Sicherheitsmodell der Xbox musste das in den 2000ern auf die harte Tour lernen: Savegame-Exploits in ungehärteten Titeln waren der Keil, der einen deutlich strengeren Kernel knackte, eine Geschichte, die wir bei what the original Xbox hack teaches about hardware security models behandelt haben. Zwanzig Jahre später funktioniert derselbe Trick immer noch.
Der Integer-Overflow, der die erste Tür öffnete
Der SmileBASIC-Bug ist ein Lehrbuchbeispiel für einen Integer-Overflow bei Bounds-Checks, genau die Sorte Fehler, die jede Checkliste für sichere Code-Reviews abfangen soll und die trotzdem laufend ausgeliefert wird. Der Array-Entfernungsbefehl des Interpreters prüfte seine Argumente so:
// intent: ensure the removed range is inside the array
if (position + count <= array.length) {
memmove(backing + position * 8, // start of the gap
backing + (position + count) * 8, // elements after the gap
(length - position) * 8); // bytes to copy
}
Wenn du REMOVE R, &H60000000, &H3FFFFFFF auf einem Array mit vier Elementen aufrufst, läuft position + count über und landet bei 0x9FFFFFFF. Als vorzeichenbehafteter 32-Bit-Wert ist das tief negativ, also problemlos „kleiner“ als die Länge. Auch die Multiplikationen mit 8 laufen über, und am Ende wird memmove(backing, backing - 8, 32) ausgeführt. Der Befehl meldet danach einen Out-of-Memory-Fehler, aber die Kopie ist bereits passiert. Und hier kommt der schöne Teil: Die 8 Bytes direkt vor jedem SmileBASIC-Array enthalten einen Pointer auf das Array selbst. Nach dem Entfernen wird R[0] als String interpretiert, dessen Längenfeld genau dieser Pointer ist, also ein astronomisch langer String, über den man lesen und schreiben kann. Beliebiger Lese- und Schreibzugriff, verpackt als Geschenk durch das eigene Speicherlayout des Interpreters.
Wenn du beruflich C oder C++ schreibst, starr dir diese Prüfung einen Moment an. position + count <= length ist das häufigste Bounds-Check-Muster in Systemcode, und es ist falsch, sobald die Summe überlaufen kann. Korrekt wäre position <= length && count <= length - position oder eine Arithmetik mit Überlaufprüfung. Ich habe Varianten dieses Bugs in einem Paket-Parser bei einem früheren Job und in einer Open-Source-Storage-Engine gefunden. Er übersteht Code-Reviews, weil er offensichtlich richtig aussieht. Das ist die Lücke zwischen Spezifikation und Implementierung im Kleinen: Die Spezifikation sagt „Bereich validieren“, die Implementierung validiert etwas Benachbartes, und genau dieser Unterschied ist ausnutzbar. Wir haben darüber geschrieben, dass the specification-implementation gap is where bugs live; Konsolen-Interpreter sind nur der Ort, an dem motivierte Teenager solche Fehler finden statt Fuzzer.
Die Eskalation zum Dateisystem-Dienst über einen Path-Handling-Overflow
Lese- und Schreibzugriff im Userspace eines Spielprozesses bringt für sich genommen nichts. Das Microkernel-Design der Switch sorgt dafür, dass jeder Dienst ein eigener Prozess mit einer eigenen IPC-Oberfläche ist. Das wertvolle Ziel war fs, das eingebaute Dateisystem-Sysmodul, und zwar aus einem herrlich rekursiven Grund: fs ist die Komponente, die entschlüsselten Code an den Loader ausliefert. Kontrolliert man fs, kann man den Code jedes anderen Sysmoduls für die Offline-Analyse dumpen. Der Quartiermeister der Festung reicht dir selbst die Baupläne.
Der Bug war ein Heap-Overflow in der Pfad-Normalisierung. Das IPC-Kommando GetEntryType normalisiert sein Pfadargument, und Pfade mit einer Verzeichnis-Eltern-Komponente samt Backslash, also /..\, landen in einem Helfer, der einen festen Puffer von 0x301 Bytes allokiert und den Pfad dann ohne ausreichende Längenprüfung hineinkopiert. Ein genügend langer Pfad läuft über den Puffer hinaus in benachbarte Heap-Objekte. Der Kommentar des Forschers zu diesem Fund bestand aus einem Wort: „lol.“
Dieses „lol“ sollte wehtun, denn Pfad-Normalisierung ist genau die unglamouröse Plumbing-Logik, die nie die Sicherheitsprüfung bekommt, die Kryptografie bekommt. Der Exploit machte aus dem Overflow vollständigen beliebigen Lese- und Schreibzugriff, indem er ein benachbartes Cache-Objekt korrumpierte, dessen Dirty-Bit Datei-I/O über einen vom Angreifer kontrollierten Pointer umleitet, ein sauberes Heap-Grooming-Primitiv. Dann fand der Exploit per Speicherscan das Verbindungsobjekt des eigenen Prozesses in den Tabellen von fs und überschrieb die gespeicherte Prozess-ID mit 1. Damit bekam der Spielprozess „FullPermission“ über das Dateisystem. Man beachte, was hier passiert ist: Keine einzige kryptografische Prüfung wurde gebrochen. Das Berechtigungsmodell wurde durch Daten im Speicher durchgesetzt, und der Angreifer konnte den Speicher beschreiben.

Signaturprüfungen umgehen, indem das Dateisystem mitten im Ablauf getauscht wird
Das cleverste Glied der Kette ist dasjenige, das Code-Signing umgeht, ohne auch nur einen einzigen Schlüssel anzufassen. Der Programm-Loader (ldr) prüft vor dem Start zwei Signaturen: die Signatur von Nintendo über die Prozess-Berechtigungsmetadaten und den Entwicklerschlüssel sowie die Signatur des Entwicklers über den Header-Hash des Content-Archivs. Theoretisch legt dieser Hash transitiv jedes Byte der ausführbaren Datei fest. In der Praxis gibt es eine Lücke: Es ist Aufgabe von fs, sicherzustellen, dass das ausgelieferte Dateisystem tatsächlich dem Hash entspricht, den es bereits gemeldet hat, und der Angreifer hatte beliebigen Lese- und Schreibzugriff in fs.
Also wartet der Exploit, bis der Loader das ausführbare Dateisystem angefordert hat und fs bereits den gültigen Hash und die Signatur übergeben hat. Dann tauscht er das Dateisystem-Objekt hinter dem IPC-Handle des Loaders gegen ein präpariertes aus, das die echten, signierten Metadaten-Datei ausliefert, aber eine beliebige Ersatz-Executable. Die Signaturprüfungen bestehen, weil sie Bytes prüfen, die der Angreifer gar nicht verändern musste. Das Timing erzwingt der Exploit, indem er das Handle umschreibt, auf das fs für die Verbindung des Loaders wartet, und zwar auf ein Event, das der Angreifer aus dem Spielprozess heraus kontrolliert. So wird aus einer Race Condition eine deterministische Zwischenschaltung.
Die kryptografische Kette war vom Header-Hash bis zum Inhalt der Executable sauber, zumindest auf dem Papier. Das eine Glied, das nur als Laufzeitannahme existierte, nämlich „der Prozess, der die Dateien ausliefert, ist ehrlich“, war das Glied, das brach.
Das ist ein Time-of-Check/Time-of-Use-Bug im Gewand eines Trenchcoats, und es ist die übertragbarste Lektion im ganzen Artikel. Immer wenn Komponente A Daten validiert und Komponente B später über eine veränderliche Referenz darauf handelt, existiert ein Swap-Fenster. Ich habe einmal ein internes Deployment-System geprüft, das einen Container-Image-Digest validierte und dann beim Rollout per Tag aus der Registry zog. Gleicher Bug, anderer Hut. Die Lösung ist in beiden Fällen dieselbe: am Punkt der Nutzung validieren oder dem Verbraucher ein unveränderliches, verifiziertes Objekt übergeben statt eines Handles auf etwas, dessen Ehrlichkeit er einem Dritten anvertrauen muss. Defensives Design, das davon ausgeht, dass jede Zwischenstation kompromittiert sein könnte, ist keine Paranoia. Auf einer Maschine, auf die der Angreifer bereits teilweise schreiben kann, ist es schlicht akkurate Modellierung. Das ist das Kernargument in defensive engineering for systems that resist catastrophe, und dieser Exploit ist eine saubere Fallstudie dafür.
Ein Kernel-Handle ausleihen und fs von innen patchen
Der letzte Trick ist klein und schön zugleich. Um den eigenen Code des fs-Prozesses zu patchen, brauchte der Exploit ein Kernel-Handle darauf. Er bekam es, indem er ein 4-Byte-Handle-Feld im Speicher mit dem Magic-Wert 0xffff8001 überschrieb, den der Kernel als „Handle des aktuellen Prozesses“ interpretiert. Der Spielprozess rief eine harmlose API auf, erhielt sein eigenes Prozess-Handle zurück und reichte es an einen Rogue-Prozess weiter, der mit den Rechten des Code-Loading-Sysmoduls lief. Dieser Prozess nutzte legitime Kernel-Aufrufe, um den Code von fs zu mappen und zu patchen. Jede einzelne Operation war autorisiert. Die Komposition war eine vollständige Kompromittierung des vertrauenswürdigsten Prozesses des Geräts.
Wenn dir das bekannt vorkommt, liegt es daran, dass das Confused-Deputy-Problem überall auftaucht, sobald man danach sucht: sudo-Regeln, die ein harmloses Binary mit schreibbarer Konfiguration erlauben, Cloud-IAM-Rollen, die sich an einen mächtigeren Dienst weitergeben können, Build-Systeme, die alles signieren, was der CI-Job ihnen reicht. Die Seite der Rechteerweiterung haben wir bei the history of Unix privilege escalation behandelt. Die Mechanismen ändern sich, die Form des Bugs nicht. Handles, Capabilities und Tokens sind nur so stark wie die Integrität des Speichers, in dem sie liegen.
Was Designer von Secure Boot immer wieder falsch machen
Tritt man einen Schritt zurück, zeigt die ganze Kette ein durchgängiges Muster, dasselbe, das hinter den meisten ernsthaften Unternehmenseinbrüchen steckt, die ich begutachtet habe: Der verifizierte Kern hat gehalten, alles drumherum nicht. Nintendos Boot-ROM, Signaturschema und Kernel-Schutzmechanismen waren in Ordnung. Gefallen ist ein Bounds-Check im Interpreter, ein Puffer bei der Pfad-Normalisierung, ein Berechtigungsfeld im beschreibbaren Speicher, ein TOCTOU-Fenster zwischen zwei vertrauenswürdigen Diensten und eine Handle-Tabelle, die Kernel-Pointer aus userspace-beschreibbaren Strukturen übernahm. Nichts davon ist exotisch. Alles davon ist die Sorte Fehler, die Reviews übersteht, weil jeder Einzelfall lokal vernünftig aussieht.
- Prüfe den langweiligen Code. Pfad-Normalisierer, Längenprüfungen und Handle-Tabellen sind der Ort, an dem Exploit-Ketten tatsächlich beginnen, nicht die Kryptografie. Plane Review-Zeit proportional zur Erreichbarkeit für Angreifer ein, nicht proportional zur algorithmischen Raffinesse.
- Behandle jede Lücke bei den Schutzmechanismen als Roadmap. Wenn ein Prozess im System kein PAC/CFI hat, etwa eine Kompatibilitätsschicht, ein Drittanbieter-Binary oder ein Legacy-Interpreter, dann geh davon aus, dass er der Einstiegspunkt ist, und modelliere, was er erreichen kann.
- Schaff Vertrauen durch Position ab. Eine Komponente, die Daten validiert und danach ein veränderliches Handle weiterreicht, hat ein Swap-Fenster geschaffen. Gib unveränderliche, selbstverifizierende Objekte weiter oder prüfe erneut am Punkt der Nutzung.
- Geh davon aus, dass beschreibbarer Speicher überall beschreibbarer Speicher ist. Berechtigungsflags, PID-Felder und Handle-Werte im vom Angreifer erreichbaren Speicher gehören zu deiner Angriffsfläche, nicht zu deinem Durchsetzungsmechanismus.
Die andere unbequeme Erkenntnis betrifft die Werkzeuge. Der Exploit wurde mit einem KI-Agenten entwickelt, der über KVM-over-IP-Geräte physischen Zugriff auf die Hardware hatte: Video rein, Controller-Eingaben per USB und ein Bluetooth-Hebel, der nach jedem Absturz den Power-Knopf drückt. Mehrere Agenten-Instanzen bearbeiteten parallel verschiedene Angriffsflächen, rund um die Uhr, zwei Wochen lang. Was man auch von KI-gestützter Sicherheitsforschung hält: Die Ökonomie der Schwachstellenfindung auf geschlossener Hardware hat sich gerade verschoben. Bugs, die früher einen geduldigen Experten brauchten, brauchen jetzt einen geduldigen Experten, der drei unermüdliche Assistenten beaufsichtigen kann. Wenn dein Bedrohungsmodell davon ausging, dass sich niemand die Mühe macht, dieses Sysmodul zu reverse-engineeren, dann ist diese Annahme diesen Monat abgelaufen.
Die Kette ist das Produkt
Man ist versucht, einen Jailbreak-Bericht als Liste von Nintendos Fehlern zu lesen. Lies ihn lieber als Spiegel. Jedes System, das ich je mitverteidigt habe, hatte ein Gegenstück zu SmileBASIC (die Legacy-Komponente, die niemand gehärtet hat), ein Gegenstück zum Pfadpuffer (den Plumbing-Code, der seinen Annahmen entwachsen ist) und ein Gegenstück zur Loader-Race (die Validierung, die einem veränderlichen Zwischenglied vertraut hat). Der Vorteil der Angreifer liegt nicht in Genialität an einem einzelnen Schritt. Sie denken in Ketten, während Verteidiger in Komponenten denken.
Die Übung für dein Team in dieser Woche lautet also nicht „Könnte jemand unsere Signaturen fälschen?“. Sie lautet: Wähle deinen vertrauenswürdigsten internen Dienst, nimm an, dass der Angreifer bereits beliebigen Schreibzugriff auf den am wenigsten gehärteten Prozess hat, der mit ihm spricht, und geh die Kette Schritt für Schritt durch. Wo landet die erste Eskalation? Was kann diese Identität dann erreichen? Dein eigenes fs-Sysmodul wirst du schneller finden, als dir lieb ist. Besser du findest es als jemand mit einem KVM, einem Bluetooth-Hebel und zwei Wochen Geduld.


