KVM-XJ Hotpatch
dump_tool.py sucht automatisch Offsets. Wie man es verwendet, steht im Code.
Schwachstellenübersicht
CVE-2026-53359 (Januscape) ist eine Use-After-Free (UAF) Schwachstelle im KVM Shadow MMU des Linux-Kernels.
- Latenzzeit: 16 Jahre (August 2010 bis Juli 2026)
- Betroffene Systeme: Alle Intel/AMD x86 Systeme mit aktivierter verschachtelter Virtualisierung
- Schweregrad: Hoch. Ein Root-Benutzer innerhalb einer VM kann zum Host entkommen und Root-Rechte auf dem Host erlangen.
- Auswirkung: Host-Kernel-Panic (DoS) oder vollständige VM-Escape (kein öffentlicher POC)
Schwachstellenprinzip
Die Funktion kvm_mmu_get_page() vergleicht bei der Suche nach wiederverwendbaren Schattenseitentabellen in der Hashtabelle nur die gfn (Gast-Physikalische-Seitenrahmennummer), aber nicht die role (Seitentabellen-Rolle/MMU-Rolle), was dazu führt, dass Seitentabellen mit nicht übereinstimmenden Rollen fälschlicherweise wiederverwendet werden, was einen UAF auslöst.
Vor dem Patch (anfällig):
┌──────────────────────────────────────┐
│ kvm_mmu_get_page() │
│ Hashtabelle durchlaufen │
│ if (child->gfn == gfn) │
│ return child ← nur gfn verglichen!│
│ Wiederverwendung auch bei │
│ Rollenkonflikt → UAF! │
│ Neue Seite zuweisen (sicher) │
└──────────────────────────────────────┘
Im Upstream-Kernel-Commit 81ccda30b4e8 wurde eine Zeile zum Rollenvergleich hinzugefügt:
// Vor dem Patch
if (... && spte_to_child_sp(*sptep)->gfn == gfn)
// Nach dem Patch
if (... && spte_to_child_sp(*sptep)->gfn == gfn
&& spte_to_child_sp(*sptep)->role.word == role.word)
Meine Reparaturmethode
Methode: text_poke binäres Hotpatch (NOP-Patch)
Es wird weder der Kernel-Quellcode geändert noch eine Funktion ersetzt, sondern direkt die anfällige Anweisung im laufenden Kernel-Speicher modifiziert.
Prinzip
Auf Binärebene:
Offset 0x104: 49 39 47 28 cmp %rax, 0x28(%r15) ← vergleicht gfn
Offset 0x108: 0f 84 9f 01 00 00 je +0x19f ← bei Treffer springen und wiederverwenden
Nach dem Patch:
Offset 0x108: 66 0f 1f 44 00 00 NOP × 6 ← tut nichts
Nach dem Patch:
┌──────────────────────────────────────┐
│ kvm_mmu_get_page() │
│ Hashtabelle durchlaufen │
│ if (child->gfn == gfn) │
│ NOP (Sprung gelöscht, gleitet vorbei)│
│ Neue Seite zuweisen (erzwingt │
│ sicheren Pfad) │
│ → Keine Wiederverwendung = │
│ kein UAF = Schwachstelle behoben │
└──────────────────────────────────────┘
Technische Umsetzung
- Adresse der Funktion
kvm_mmu_get_page über kallsyms_lookup_name finden
text_poke Funktion über kallsyms_lookup_name finden (Kernel-Code-Hotpatch-API)
- Alle CPUs mit
stop_machine anhalten, um sichere Änderung zu gewährleisten
- Mit
text_poke die 6-Byte je-Anweisung durch 6-Byte NOP ersetzen
- Beim Entladen die ursprüngliche Anweisung mit
text_poke wiederherstellen
Warum keine anderen Lösungen
Auswirkungsbewertung
Bereitstellungsanforderungen
| Bedingung | Beschreibung |
|---|
| Kernel-Version | Linux 4.18+ (CentOS 8 / RHEL 8 / Rocky 8 usw.) |
| Build-Umgebung | kernel-devel + gcc + make |
Kompilieren und Laden
# Kompilieren
make
# Hotpatch laden
insmod KVM-XJ.ko
# Status anzeigen
dmesg | grep KVM-XJ
cat /sys/module/kvm_intel/parameters/nested # sollte immer noch 1 sein
lsmod | grep KVM_XJ
# Entladen (ursprünglichen Code wiederherstellen)
rmmod KVM_XJ
Anpassung an andere Kernel
Unterschiedliche Kernelversionen haben unterschiedliche Compileroptimierungen, daher variiert der Offset der je-Anweisung. Verwenden Sie dump_tool.py zur automatischen Analyse:
# 1. kvm.ko vom Host herunterladen
scp root@Host:/lib/modules/.../kvm.ko.xz .
xz -d kvm.ko.xz
# 2. Analysetool ausführen
python dump_tool.py kvm.ko
# 3. Das Tool gibt den Patch-Offset aus; ändern Sie 0x108 in KVM-XJ.c
Verifizierte Kernel
| Kernel-Version | je-Offset | Status |
|---|
| 4.18.0-496.el8.x86_64 | 0x108 | Getestet |
| 4.18.0-358.el8.x86_64 | 0xe8 | Getestet |
Hinweise
- Wenn Sie nicht wissen, wie man es verwendet oder den Code nicht verstehen, wird von der Nutzung abgeraten.