2 Punkte von GN⁺ 4 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • GhostLock (CVE-2026-43499) ist eine Kernel-Schwachstelle, die in Linux 2.6.39 eingeführt und in 7.1 behoben wurde. Ein lokaler, nicht privilegierter Angreifer kann allein mit gewöhnlichen Threading-Systemaufrufen eine Stack-UAF auslösen und sie für Root-Rechte und Container-Escape ausnutzen.
  • remove_waiter() im Requeue-PI-Proxy-Pfad löscht pi_blocked_on von current statt von der tatsächlich wartenden Task, sodass in der Task nach der Rückkehr in den Userspace ein Zeiger auf einen freigegebenen Stack-Frame verbleibt.
  • Mit drei Futexen und drei Threads wird eine PI-Abhängigkeits-Schleife erzeugt, um ein -EDEADLK-Rollback auszulösen. Anschließend wird in dem kontrollierbaren Stack-Puffer von PR_SET_MM_MAP ein gefälschter rt_mutex_waiter aufgebaut, um eine eingeschränkte Pointer-Schreibprimitive zu erhalten.
  • Der Exploit nutzt prefetch, um Basisadressen für KASLR und physmap zu finden, platziert gefälschte Strukturen und einen ROP-Stack in der CPU entry area (CEA) und überschreibt dann inet6_protos[IPPROTO_UDP], um mit IPv6-UDP-Loopback-Paketen den Kontrollfluss zu kapern.
  • Die Forschenden erhielten bei Google kernelCTF 92.337 US-Dollar für einen zu 97 % stabilen Exploit zur Rechteausweitung und zum Container-Escape; alle ungepatchten Linux-Distributionen sollten auf die neueste LTS-Version aktualisiert werden.

Betroffener Bereich und Überblick über die Schwachstelle

  • GhostLock ist eine von VEGA entdeckte Linux-Kernel-Schwachstelle, die von lokalen, nicht privilegierten Benutzern ohne zusätzliche Rechte oder User-Namespaces ausgelöst werden kann.
  • Sie wurde durch die rtmutex-Überarbeitung in 8161239a8bcc eingeführt; betroffen ist der Bereich von v2.6.39-rc1 bis v7.1-rc1.
  • Behoben wurde sie im April 2026 in 3bfdc63936dd; erforderlich ist lediglich die Kernel-Konfiguration CONFIG_FUTEX_PI=y.
  • Angreifer können ihre Rechte über folgende Schritte ausweiten:
    • Allein mit gewöhnlichen Threading-Systemaufrufen erhalten sie einen dangling Kernel-Zeiger, der auf Kernel-Stack-Speicher zeigt.
    • Sie erzeugen eine eingeschränkte Primitive, mit der sich ein Pointer oder acht Byte 0 an nahezu beliebige Adressen schreiben lassen.
    • Sie kapern eine Funktionstabelle, übernehmen den Kontrollfluss und erlangen Root-Rechte.
  • Da alle ungepatchten Linux-Distributionen betroffen sind, sollte auf die neueste LTS-Version aktualisiert werden.

Warum remove_waiter() die falsche Task bereinigt

  • remove_waiter() in kernel/locking/rtmutex.c wurde ursprünglich für den Pfad geschrieben, in dem ein blockierter Thread seinen eigenen Wartezustand direkt bereinigt.
  • Im normalen Slow-Path ist das laufende current die Besitzer-Task des Waiters, daher ist das Löschen von current->pi_blocked_on korrekt.
  • Im Requeue-PI-Proxy-Pfad reiht rt_mutex_start_proxy_lock() jedoch im Namen einer anderen schlafenden Task einen rt_mutex_waiter in die Queue ein und rollt dies bei einem Fehler zurück.
    • Dabei ist current der Requeuer, der FUTEX_CMP_REQUEUE_PI aufgerufen hat.
    • Der eigentliche Waiter ist eine separate Task, die in FUTEX_WAIT_REQUEUE_PI schläft.
  • Wenn __rt_mutex_start_proxy_lock() -EDEADLK zurückgibt, entfernt remove_waiter() den Waiter vom Lock, setzt aber nur current->pi_blocked_on auf NULL.
  • Das pi_blocked_on des eigentlichen Waiters zeigt weiter auf den rt_mutex_waiter in seinem eigenen Kernel-Stack, und wenn der Waiter in den Userspace zurückkehrt, gilt dieser Stack-Frame als freigegeben.
  • Sobald später eine PI-Chain-Walk durch diese Task läuft, wird ein freigegebenes Stack-Objekt dereferenziert.
  • lockdep prüft nur, ob irgendein pi_lock gehalten wird, aber nicht, zu wessen Lock es gehört, und erkennt diesen Fehler daher nicht.

Drei-Futex-Zyklus zur Erzeugung des -EDEADLK-Rollbacks

  • Um den Fehlerpfad zu erreichen, wird mit drei Futexen und drei Threads eine PI-Abhängigkeits-Schleife aufgebaut.
    • f_pi_chain: ein PI-Futex, den der Waiter zuerst sperrt
    • f_pi_target: ein PI-Futex, den der Owner zuerst sperrt und der das Requeue-Ziel ist
    • f_wait: ein normaler Futex, auf dem der Waiter mit FUTEX_WAIT_REQUEUE_PI wartet
  • Die Trigger-Reihenfolge ist wie folgt:
    1. Der Waiter sperrt f_pi_chain und blockiert dann in FUTEX_WAIT_REQUEUE_PI(f_wait -> f_pi_target); dabei wird ein rt_mutex_waiter auf seinem Kernel-Stack abgelegt.
    2. Der Owner sperrt f_pi_target und blockiert dann auf f_pi_chain, das vom Waiter gehalten wird.
    3. Der Main-Thread ruft FUTEX_CMP_REQUEUE_PI(f_wait -> f_pi_target) auf.
  • Wenn das Proxy-Requeue versucht, den Waiter an f_pi_target zu hängen, schließt sich der Zyklus waiter → f_pi_target → owner → f_pi_chain → waiter.
  • Die PI-Chain-Walk liefert -EDEADLK zurück und führt das fehlerhafte Rollback aus, wodurch der Waiter mit einem dangling pi_blocked_on aufwacht.
  • Entscheidend ist, dass der Requeuer das Rollback ausführt, solange der Waiter das Stack-Objekt noch besitzt; nachdem der Zyklus vollständig ist, läuft der Prozess selbstständig weiter.
  • Sobald der Waiter in den Userspace zurückkehrt, besteht kein Zeitdruck mehr, und später kann jederzeit mit sched_setattr() eine Chain-Walk ausgelöst werden.
  • Die Konstruktion verwendet drei Threads, aber die eigentliche UAF-Race lässt sich auch auf nur einem CPU-Kern auslösen.

Erste Primitive, die die Stack-UAF liefert

  • Der dangling Pointer zeigt auf den rt_mutex_waiter, der zuvor im FUTEX_WAIT_REQUEUE_PI-Frame lag.
  • Wenn auf derselben Stack-Tiefe derselben Task erneut kontrollierbare Bytes platziert werden, kann der Kernel dazu gebracht werden, sie als gefälschten rt_mutex_waiter zu dereferenzieren.
  • Je nachdem, wie die gefälschte Struktur angeordnet ist, ergeben sich mit einem einzelnen Zugriff zwei zentrale Primitive:
    • Es lässt sich ein Pointer an eine nahezu beliebige Adresse mit Einschränkungen schreiben.
    • Es lassen sich acht Byte 0 an eine nahezu beliebige Adresse mit Einschränkungen schreiben.
  • Vor dem Schreibvorgang werden mehrere Pointer-Dereferenzen und Integritätsprüfungen durchgeführt, aber wenn die Bedingungen erfüllt sind, kehrt der Kernel auch nach dem Schreibvorgang ohne Absturz normal zurück.
  • Um den Exploit zu vervollständigen, sind die Wiederverwendung des Stack-Frames, das Bestehen der Strukturprüfungen des gefälschten Waiters und die Auswahl eines Ziels, das die Schreibbeschränkungen erfüllt, alle erforderlich.

Wiederverwendung des freigegebenen Stack-Frames mit PR_SET_MM_MAP

  • Der waiter ruft unmittelbar nach der Rückkehr aus dem futex-Systemaufruf prctl(PR_SET_MM, PR_SET_MM_MAP, ...) auf.
  • prctl_set_mm_map() kopiert das vom Benutzer gelieferte auxv in den Stack-Puffer fester Größe unsigned long user_auxv[AT_VECTOR_SIZE].
  • Da dieser Puffer in einer ähnlichen Stack-Tiefe wie der freigegebene waiter liegt, überlappt ein großer, ausgerichteter und kontrollierbarer qword-Block den früheren rt_mutex_waiter.
  • Der überlappende Bereich des auxv wird wie folgt aufgebaut:
    • tree: Macht daraus einen rb-Knoten, der den beim Löschen gewählten Child-Pointer W0_BASE zur Tree-Root hochzieht.
    • task: Wird auf &init_task gesetzt, damit die Dereferenzierung bei der Kettensuche sicher durchlaufen wird.
    • lock: Wird auf &inet6_protos[IPPROTO_UDP] - 8 gesetzt, um das Schreibziel zu treffen.
    • wake_state: Wird auf 0 gesetzt.
  • Das auxv wird in einem memfd abgelegt und so positioniert, dass der Kopiervorgang eine Seitengrenze überschreitet; anschließend erzeugt ein Geschwister-Thread während der prctl-Ausführung mit fallocate(PUNCH_HOLE) auf der hinteren Seite eine Race, um die Zeit von copy_from_user zu verlängern.
  • Der consumer-Thread auf einer anderen CPU ruft sched_setattr() auf dem waiter auf, während der gefälschte waiter noch auf dem Stack liegt, und durchsucht so die PI-Kette.
  • Auch andere Systemaufrufe mit großen kontrollierbaren lokalen Stack-Variablen wie clone, setsockopt, pselect oder keyctl können dieselbe Rolle übernehmen.
  • prctl wurde gewählt, weil der Puffer groß und ausgerichtet ist und kein Namespace benötigt wird; weitere Kandidaten sind im veröffentlichten PoC-Code enthalten.

Erzeugen eines eingeschränkten Pointer-Schreibens durch rb-tree-Löschung

  • Selbst bei Kontrolle über den gefälschten waiter entsteht nicht sofort ein vollständiger beliebiger Write; die Kettensuche führt folgenden Pfad aus:
    • Findet den gefälschten waiter über task->pi_blocked_on.
    • Findet die gefälschte rt_mutex_base über fake waiter->lock.
    • rt_mutex_dequeue(lock, waiter) führt eine rb-tree-Löschung in lock->waiters aus.
  • Dabei wird ausgenutzt, dass beim Löschen eines Root-Knotens mit nur einem Kind dieses Kind in den Root-Slot geschrieben wird.
  • Wenn lock auf target - 8 gesetzt wird, werden benachbarte Daten als folgende rt_mutex_base-Felder interpretiert:
    • target - 8: wait_lock, das als ungesperrt gelesen werden muss
    • target: waiters.rb_root.rb_node, das überschrieben wird
    • target + 8: waiters.rb_leftmost
    • target + 16: owner
  • Der tatsächlich ausgeführte einzelne Write ist somit *(uint64_t *)target = W0_BASE.
  • Die Zieladresse muss grob folgende Bedingungen erfüllen:
    • Die unteren 32 Bit von target - 0x08 müssen 0 sein.
    • Der 64-Bit-Wert an target + 0x08 muss 0 sein.
    • Beim owner-Pointer an target + 0x10 muss der Wert ohne die unteren Flags 0 sein.
  • Wenn das vordere qword wie ein gesperrter spinlock aussieht, schlägt trylock fehl und der Ablauf endet ohne Write.
  • Wenn der nachfolgende Wert auf einen unkontrollierten top waiter oder owner zeigt oder auf einen nicht gemappten Wert verweist, kann ein Kernel-Panic auftreten.
  • W0_BASE muss gültig bleiben, bis Vergleich, Requeueing, Prioritätsaktualisierung und Wakeup ohne Owner abgeschlossen sind; daher wird der Direct-Map-Alias des CEA verwendet.

prefetch-Leak und CPU entry area

  • KASLR- und physmap-Basisadresse finden

    • Die Ausführungszeit von prefetch für eine bestimmte Adresse unterscheidet sich danach, ob diese Adresse in den aktuellen Seitentabellen gemappt ist.
    • Misst ein nicht privilegierter Prozess die Laufzeit im Kernel-Adressraum, kann er gemappte Positionen abschätzen; die Details sind im prefetch-Paper beschrieben.
    • Da die Entropie der Basisadresse des Standard-Linux-Kernel-Images nur etwa 9 Bit beträgt, kann die KASLR-Basisadresse durch wiederholte Messungen mit nahezu 100 % Zuverlässigkeit wiederhergestellt werden.
    • Theoretisch sind CPUs mit prefetch und ohne geeignete KPTI betroffen, praktisch wird die Technik aber vor allem auf x86 mit deaktiviertem KPTI genutzt.
    • kernelCTF-Images haben KPTI deaktiviert; selbst wenn KPTI aktiviert ist, lassen sich prefetch und EntryBleed kombinieren, um über den Trampoline die Basisadresse des Kernel-Images wiederherzustellen.
  • Umgehung der CEA-Adressrandomisierung

    • Die CPU entry area (CEA) ist eine x86-spezifische Struktur pro CPU, die Stacks und Registerkontext für Eintritts- und Ausnahmebehandlung speichert.
    • Löst ein nicht privilegiertes Programm eine Software-Ausnahme aus, kann es seinen eigenen Registerkontext in pt_regs des CEA-Ausnahmestacks schreiben und so etwa 120 Byte zusammenhängenden kontrollierbaren Speicher erzeugen.
    • Vor Linux 6.2 war die virtuelle CEA-Adresse vollständig fest, sodass sie direkt für gefälschte Strukturen, das Abfangen von Nebeneffekten bei Pointer-Dereferenzierungen und den Aufbau eines ROP-Stacks verwendet werden konnte.
    • Nach der Veröffentlichung von Project Zeros Bringing back the stack attack wird die virtuelle CEA-Adresse seit Linux 6.2 stark randomisiert.
    • Die virtuelle CEA-Adresse jeder CPU wird unterschiedlich randomisiert, die physische Adresse bleibt jedoch fest; kennt man also die physmap-Basisadresse, kann man den Direct-Map-Alias berechnen.
    • Durch die Kombination von prefetch, Normalisierung der Kandidatengrenzen und Prüfung erwarteter CEA-Seiten wird ein benachbarter Alias ausgeschlossen und cea_direct = physmap_base + CPU1_CEA_BASE ermittelt.
    • In der kernelCTF-LTS-Umgebung 6.12.80 mit 3,5 GB Boot-Speicher beträgt der relevante Offset 0x11c517000(+0x1f58).

Wiederverwendung des CEA für gefälschte waiter und Folgeobjekte

  • Vor dem ersten Write werden in W0 des CEA ein in sich konsistenter gefälschter waiter und ein Lock platziert:
    • task wird auf &init_task gesetzt.
    • prio erhält einen gültigen Wert.
    • wait_lock des Lock wird so gestaltet, dass es ungesperrt erscheint.
    • owner wird so aufgebaut, dass Dequeue, Requeueing, Prioritätsaktualisierung und Wakeup sicher durchlaufen werden.
  • Nachdem der rb-tree-Write abgeschlossen ist, muss W0 nicht länger ein waiter sein, sodass das CEA mit der Struktur neu befüllt werden kann, die das überschriebene Ziel verlangt.
  • Das CEA ist mit rund 120 Byte klein, aber effizient, weil sich Daten an einer berechenbaren festen Kernel-Adresse platzieren lassen.
  • NPerm und kernelsnitch können dieselbe Rolle auch in größerem Speicherbereich erfüllen.
  • Der Exploit nutzt einen einzelnen CEA-Bereich nacheinander oder gleichzeitig als gefälschten rt_mutex_waiter, gefälschtes Lock, inet6_protocol, JOP-/Stack-Pivot-Slots und finalen ROP-Stack.

Übernahme des Kontrollflusses über inet6_protos[IPPROTO_UDP]

  • Auf gewöhnlichen x86_64-Linux-Systemen kann man nach der Ermittlung der KASLR-Basisadresse einen kurzen Pfad wählen, der eine passende Funktionstabelle oder ein Objekt, das diese enthält, überschreibt.
  • Der Bereich um inet6_protos[IPPROTO_UDP] im beschreibbaren Datensegment erfüllt die nötigen Einschränkungen auf natürliche Weise.
    • inet6_protos[16] == NULL wird zum entsperrten Zustand eines gefälschten wait_lock
    • inet6_protos[17] == &udpv6_protocol ist das eigentliche Ziel zum Überschreiben
    • inet6_protos[18] == NULL wird zu einem gefälschten rb_leftmost
    • inet6_protos[19] == NULL wird zu einem gefälschten owner
  • Nach Abschluss des Schreibvorgangs zeigt inet6_protos[IPPROTO_UDP] auf ein gefälschtes inet6_protocol innerhalb der CEA-Seite.
  • Durch erneutes Besprühen der CEA wird die Struktur wie folgt aufgebaut.
    • handler: wird auf das erste Pivot-Gadget gesetzt
    • err_handler: wird nicht verwendet
    • flags: wird auf INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL gesetzt
  • Sendet man nach einem connect auf ::1 ein IPv6-UDP-Loopback-Paket, das Daten schreibt, ruft der Kernel den gefälschten handler auf und der Programmzähler kann kontrolliert werden.

Kurzer Pivot und DirtyMode-Privilegieneskalation

  • Beim Ziel lts-6.12.80 von Google kernelCTF wurde kein geeignetes einzelnes Stack-Pivot-Gadget gefunden; daher wird nach einem zusätzlichen Load/Call die CEA-Adresse in rbp gelegt und anschließend mit mov rsp, rbp; pop rbp; ret gepivotet.
  • ret2usr oder ein vollständiges Überschreiben von /proc/%P/fd/x benötigen etwa 10 Gadget-Qwords und sind damit für den begrenzten CEA-Speicher zu groß.
  • Im letzten Schritt wird DirtyMode verwendet, das mit einem einzigen Schreibvorgang die Berechtigungsbits ändert und den Rest des Ablaufs im User Space ausführt.
  • Ziel des Schreibvorgangs sind coredump_sysctls[1].mode in den Kernel-Daten, also die Zugriffsrechte des core_pattern-Sysctl.
  • Da dieselbe KASLR-Slide wie beim Kernel-Image verwendet wird, lässt sich die Adresse berechnen; ein Wert, bei dem das zweitniedrigste Bit als Schreibbit gesetzt ist, genügt.
  • Mit einer kurzen Kette aus pop reg; mov [reg], reg; ret wird der mode-Wert geändert, und per msleep wird der übernommene Thread sicher angehalten.
  • Sobald /proc/sys/kernel/core_pattern für alle Benutzer beschreibbar ist, kann ein nicht privilegierter Prozess |/proc/%P/fd/666 %P hineinschreiben und den Helper zum Absturz bringen, sodass der Kernel das Binärprogramm des Angreifers mit Root-Rechten ausführt.
  • Der anfängliche rb-tree-Schreibvorgang kann coredump_sysctls[1].mode wegen der Einschränkungen beim Platzieren nicht direkt erreichen; die Änderung des mode erfolgt daher in einer kurzen ROP-Phase.

Gesamtablauf des Exploits und Ergebnis

  • Der Angriff läuft in folgender Reihenfolge ab.
    1. Mit prefetch werden die Slide des Kernel-Images und die Basisadresse von physmap geleakt.
    2. Mit GhostLock bleibt ein baumelnder rt_mutex_waiter in pi_blocked_on des waiter zurück.
    3. Mit PR_SET_MM_MAP wird derselbe Kernel-Stack-Frame wiederverwendet, um einen gefälschten waiter zu erzeugen.
    4. Über das Löschen des rtmutex-rb-tree wird ein CEA-Pointer in inet6_protos[IPPROTO_UDP] geschrieben.
    5. In der CEA werden ein gefälschtes inet6_protocol, ein Pivot-Slot und ein ROP-Stack platziert.
    6. Ein IPv6-UDP-Loopback-Paket ruft den überschriebenen handler auf.
    7. Mit DirtyMode werden die mode-Bits von core_pattern geändert und die Privilegieneskalation im User Space abgeschlossen.
  • In der Remote-Umgebung von kernelCTF erhielt der Pfad, der CEA und DirtyMode kombiniert, die Flag in etwa 5 Sekunden.
  • Der gesamte Exploit ist im CyberMeowfia-Projekt veröffentlicht.
  • Unter Android unterscheiden sich die Wiederverwendung von Stack-Frames sowie die Umgehung von ASLR und CFI; das soll in einem separaten Folgebeitrag behandelt werden.

Alternative Pfade und Gegenmaßnahmen

  • Größerer ROP-Speicher

    • Speicher auf Basis von NPerm kann nach der Übernahme des Kontrollflusses als großer gefälschter Stack verwendet werden.
    • Auch schwergewichtigere Pfade wie der heap-KASLR-Leak von Lukas Maar sind möglich, fügen aber zusätzliche Schritte hinzu und verlängern die Ausführungszeit.
    • Bei kernelCTF ist die kürzeste und zuverlässigste Kette am vorteilhaftesten, daher wird die Kombination aus CEA und DirtyMode verwendet.
  • Kernel-Patch

    • Der endgültige Patch sperrt pi_lock auf Basis von waiter->task statt von current und löscht pi_blocked_on.
    • remove_waiter() speichert waiter_task = waiter->task und verarbeitet dann in folgender Reihenfolge.
      1. waiter_task->pi_lock sperren
      2. waiter aus der rtmutex-Warteschlange entfernen
      3. waiter_task->pi_blocked_on = NULL setzen
      4. Auch an das nachfolgende rt_mutex_adjust_prio_chain() wird waiter_task statt current übergeben
    • Ein separater Fix-Vorschlag, den die Forschenden vor v1 schickten, sah vor, dass der Aufrufer die Besitzer-Task explizit übergibt.
      • Auf Pfaden, in denen der Aufrufer selbst blockiert wird, wird current übergeben.
      • Bei Proxy-Rollback wird die Proxy-Ziel-task übergeben.
      • pi_blocked_on wird nur dann gelöscht, wenn es noch auf genau diesen waiter zeigt, und durch das pi_lock der Task geschützt.
  • RANDOMIZE_KSTACK_OFFSET

    • Der Exploit hängt davon ab, dass sich der freigegebene waiter-Frame und der nachfolgende user_auxv-Frame deterministisch überlappen.
    • Ist RANDOMIZE_KSTACK_OFFSET aktiviert, ändert sich der Stack-Offset, und dieser Schritt wird zu einem 5-Bit-Raten mit etwa 1/32 Wahrscheinlichkeit.
    • Bei den beiden eingereichten allgemeinen Zielen war diese Einstellung standardmäßig deaktiviert; beim mitigierten Ziel war sie aktiviert, daher wurde dieser Exploit-Pfad dort nicht verwendet.
  • STATIC_USERMODE_HELPER

    • STATIC_USERMODE_HELPER blockiert diesen speziellen DirtyMode-Pfad.
    • Da die Zugriffsrechte jedoch über ctl_table::mode gesteuert werden und sich die Tabelle in vorhersagbaren beschreibbaren Kernel-Daten befindet, lässt sich dieselbe Methode auch auf andere /proc/sys-Einstellungen verallgemeinern.

Zeitplan der Offenlegung

  • 18. April 2026: Die Schwachstelle und ein Entwurf des Patches werden an security@kernel.org übermittelt.
  • 20. April 2026: Die Schwachstelle wird durch einen anderen Patch behoben.
  • 4. Mai 2026: Fix v1 wird zurückportiert.
  • 30. Juni 2026: Google bestätigt die kernelCTF-Einreichung.
  • 7. Juli 2026: Die technische Analyse wird veröffentlicht.
  • Für die von VEGA entdeckte Schwachstelle gilt die übliche 90+30-Tage-Offenlegungspolitik.

1 Kommentare

 
GN⁺ 4 시간 전
Meinungen auf Hacker News
  • Getestet auf drei Geräten mit Android 9, 13 und 16 sowie unterschiedlichen Firefox-Versionen unter 150: Zwei landeten in einer Boot-Schleife und mussten in den Recovery-Modus, das dritte schaltete sich aus. Die Demo ändert das Hintergrundbild unterstützter Pixel-Geräte; die Testseite ist unter IonStack zu finden.
    Wenn man auf dem privaten Gerät Blogs oder beliebige Websites besucht, ist es sicherer, zusätzlich zum Hauptbrowser einen Chromium-basierten Browser wie Chromite zu installieren, in den Flags JavaScript und die häufig angegriffenen hardwarebeschleunigten Video-Decoder zu deaktivieren und für kaputte Websites den Lesemodus zu verwenden. Alternativ kann man auch ein dediziertes Tablet nutzen.

    • Derzeit wurde es nur auf dem Pixel 10 getestet, aber es gibt einige PRs zur Unterstützung weiterer Geräte; siehe https://github.com/NebuSec/CyberMeowfia
    • Beim Portieren des Kernel-Exploits auf andere Geräte zeigte sich, dass er sehr empfindlich darauf reagiert, wie der Compiler den Stack-Frame je Kernel-Build anordnet. Hat man erst einmal die zu einem bestimmten Build passende Stamping-Methode und die Offsets gefunden, läuft er ziemlich stabil.
    • Ich bin das Risiko eingegangen und habe es auf einem Samsung S26 Ultra ausgeführt; nach der Überprüfung mit installiertem adb werde ich die vollständigen Ergebnisse veröffentlichen.
      Beim Aufruf der Testseite erschien eine Ausgabe im Firefox-Tab, sodass der Proof-of-Concept-Code offenbar lief. Danach fror das Telefon jedoch ein und verweigerte jede Eingabe. Nur ein Neustart funktionierte; ich frage mich, wie es noch auf ein Neustart-Ereignis reagieren kann, wenn der Kernel scheinbar hängt. Der Bildschirm blieb mit einem Teil der Ausführungsausgabe eingeschaltet, bis der Bildschirmschoner aktiv wurde.
    • Es wäre großartig, wenn man das zum Rooten von Android-Geräten nutzen könnte, die sich bislang nicht rooten lassen; ich frage mich, wie das möglich wäre.
    • Die Firefox-Schwachstelle scheint CVE-2026-10702 zu sein, eine Type-Confusion im IonMonkey-JIT-Compiler: https://www.sentinelone.com/vulnerability-database/cve-2026-10702/
  • Großes Lob an die Sicherheitsforscher: Sie haben nicht nur den Exploit gefunden, sondern im Gegensatz zu copyfail auch kein Zero-Day-Skript zur lokalen Rechteausweitung veröffentlicht, das jeder sofort benutzen kann.
    Ich habe auf Rocky Linux 9 mehrere Stunden lang versucht, eine lokale Rechteausweitung (LPE) hinzubekommen, glücklicherweise ohne Erfolg. Wenn man nicht sehr viel Zeit oder außergewöhnliche Fähigkeiten hat, scheint das auf Enterprise-Distributionen in realen Angriffen schwer auszunutzen zu sein.

  • Ich frage mich, ob sich mit dieser Schwachstelle auch auf Telefonen, deren Bootloader sich normalerweise nicht entsperren lässt, der Bootloader entsperren ließe. Falls ja, könnte das eines der besten Dinge sein, die dem Android-Ökosystem je passiert sind.

  • Im Titel hätte LPE für lokale Rechteausweitung stehen sollen; dann hätten sich die meisten beruhigt wieder ihrem Wochenende widmen können.

    • Ganz so beruhigend ist das nicht. Üblicherweise meint ein lokaler Privilege-Escalation-Exploit einen Angriff, der von normalen Benutzerrechten zu Root-Rechten führt; Apps mit normalen Rechten können ohnehin schon großen Schaden anrichten, daher macht man sich meist nicht allzu große Sorgen.
      Dieser Angriff lässt sich jedoch sogar innerhalb eines stark gesandboxten Prozesses auslösen, etwa in Firefox' isoliertem Browser-Prozess. Ein Angreifer muss nur einen zweistufigen Angriff verketten: per JavaScript-Schwachstelle lokalen Code in der isolierten Sandbox ausführen und anschließend mit dieser Schwachstelle bis in den Kernel-Modus aufsteigen. Deshalb müssen sowohl Firefox als auch der Linux-Kernel aktualisiert werden.
    • Der Angriff im übergeordneten Kommentar sieht so aus, als bekäme man direkt aus JavaScript heraus Root-Rechte, tatsächlich werden aber zwei unterschiedliche Exploits verkettet.
    • Wenn ein Container-Escape möglich ist, könnte das meiner Meinung nach immer noch viele Menschen betreffen.
    • Da auch eine Type-Confusion-Schwachstelle in Firefox/IonMonkey gefunden wurde, kann schon der Besuch einer beliebigen Website sehr schnell zur Übernahme des Geräts führen.
    • Heutzutage gibt es vermutlich Hunderte Zero-Days, die für genau solche Situationen gehortet werden. Von SSH bis Node.js tauchen alle paar Wochen neue Probleme auf; wenn man nicht wirklich jegliche Kommunikation hinter WireGuard versteckt, muss man praktisch alles wie eine Remote-Schwachstelle behandeln.
  • Ab der Stelle „Google zahlte 92.337 US-Dollar als kernelCTF-Prämie“ wurde ich hellhörig.

    • Angesichts der Tragweite wirkt das nach wenig Geld. Ich frage mich, ob Unternehmen nur für Remote-Exploits große Summen zahlen.
  • Bedeutet das, dass eine Android-App über das NDK nativen Code ausführen und Root-Rechte erlangen kann? Und hilft SELinux bei der Abwehr?

    • Bei Nicht-Flaggschiff-Smartphones, die selten Updates einschließlich Kernel-Updates erhalten, scheint das in der Praxis ziemlich wahrscheinlich.
      Patches können zwar auch auf ältere Kernel zurückportiert werden, aber in den Änderungsprotokollen von Smartphone-Updates werden CVEs selten ausdrücklich genannt, sodass Schwachstellen-Scanner praktisch die einzige Möglichkeit zur Überprüfung sind. Wenn eine App aus dem Play Store oder aus externer Quelle kompromittiert wurde, könnte sie sofort Root-Rechte erlangen; daher bleibt das Prinzip wichtig, bei der Installation auf Vertrauen und Auditierbarkeit zu achten.
      Künftig könnte diese Prüfung in alle Stufen von Google Play Integrity aufgenommen werden, sodass sich auf ungepatchten Telefonen viele Apps nicht mehr installieren lassen. In Browsern, in denen sich beliebige Websites und Werbung schwer vermeiden lassen, ist es noch gravierender, weil ein Sandbox-Escape auch die App-Isolation umgeht; das ähnelt JailbreakMe unter iOS.
    • Wenn der Kernel selbst kompromittiert ist, kann SELinux nicht schützen. Auch die Android-Sandbox oder Container-Techniken wie Docker halten diesen Exploit nicht auf; ein realistisches Mittel zur Isolation ist nur vollständige Virtualisierung. Wer KVM nutzt, muss voraussetzen, dass der Patch für die letzte Woche veröffentlichte CVE-2026-53359 überall ausgerollt wurde.
      Jede App, die auf einem Linux der letzten 15 Jahre nativen Code ausführen kann, kann bis zum Eintreffen eines Kernel-Updates auf dem Gerät Root-Rechte erlangen.
  • Es ist schockierend, dass GhostLock in Linux 2.6.39 eingeführt und erst in Linux 7.1 behoben wurde.

  • Ich habe das Gefühl, die Kommentare schon am Vortag gelesen zu haben, aber die Erstellungszeiten werden alle mit höchstens 10 Stunden angezeigt. Ich frage mich, ob HNs Zeitanzeige falsch ist.

    • Das liegt sehr wahrscheinlich am Re-up-System von HN. Als dieser Beitrag erneut hochgezogen wurde, wurden die Zeitstempel der bestehenden Kommentare relativ neu berechnet; mehr dazu findet man unter https://hn.algolia.com/?dateRange=all&page=0&prefix=true&query=by%3Adang%20timestamps%20re-up&sort=byDate&type=comment.
      In der täglich durchgesehenen „underwater“-Liste – also der Liste von Beiträgen, die viele Empfehlungen bekommen haben, aber aus irgendeinem Grund nicht auf die Startseite kamen – stand dieser Beitrag ganz oben, deshalb wurde er erneut sichtbar gemacht. Das wirkt seltsam, aber bisher gibt es keine weniger verwirrende Alternative.
    • Manchmal werden ähnliche Artikel zusammengeführt und dabei auch die Kommentare zusammengelegt.