Agenda
Dieser Beitrag wurde aus dem Englischen übersetzt. Zum Originalbeitrag.
Linux1
![]()
Manchmal kann man dem Drang einfach nicht widerstehen, sich in den Code von Linux-Kernel-Modulen zu vertiefen. Ich entschied mich, mit einem Third-Party-Modul zu beginnen, das ich selbst nutze, dessen Verbreitung jedoch begrenzt ist – ein ideales Ziel.
Schon nach wenigen Stunden entdeckte ich etwas, das wie eine Schwachstelle aussah. Eine genauere Analyse bestätigte den Verdacht: Laut Code war ein Kernel Memory Overread möglich. Allerdings war das zunächst nur eine Theorie. Um sie zu verifizieren, musste ich einen Proof of Concept (PoC) erstellen.
Die Schwachstelle war nichts besonders Spektakuläres, „nur“ ein Double-Fetch-Problem, wie in der Abbildung unten dargestellt. Allerdings wusste ich nicht, ob es ausreichen würde, einfach zwei Threads zu starten, um die Race Condition auszunutzen – oder ob komplexere Techniken nötig wären. Es gab nur einen Weg, das herauszufinden.
![]()
Linux2
Arbeiten an einem PoC
Ich begann mit einem einfachen PoC, der über grundlegende Befehle mit dem Kernel-Modul kommunizieren konnte. Nachdem ich erfolgreich eine Antwort erhalten hatte, entwickelte ich einen PoC speziell für die Schwachstelle.
Meine Erwartung war klar: Das Auslösen des Bugs sollte das System abstürzen lassen. Doch beim Start des PoC passierte – nichts. Ich überprüfte mehrfach, ob die Kommunikation mit dem Modul funktionierte und der richtige Codepfad im IOCTL-Handler erreicht wurde. Trotzdem kein Absturz.
Also musste es daran liegen, dass der Double-Fetch so schnell erfolgt, dass sich die Race Condition ohne zusätzliche Tricks nicht ausnutzen lässt. Ich investierte mehrere Tage in die Recherche entsprechender Techniken. Und ja – es gibt durchaus sehr kreative Ansätze. Allerdings waren die meisten bereits gepatcht, und der Rest funktionierte in meinem Setup nicht. Letztlich legte ich das Thema zunächst auf Eis.
Neustart
Einige Monate später griff ich das Thema mit frischer Motivation wieder auf:
-
Ich könnte ein eigenes Kernel-Modul entwickeln, das dem Zielmodul ähnelt, umfangreiche Debug-Ausgaben integrieren und versuchen, den Double Fetch gezielt zu triggern.
-
Wenn das funktioniert, ließe sich derselbe Ansatz auf das Originalmodul übertragen.
-
Alternativ könnte ich den Kernel Address Sanitizer (KASAN) einsetzen – ein im Kernel integriertes Tool zur Erkennung von Speicherfehlern, auch solcher, die schwer zu reproduzieren sind. [docs.kernel.org]
Da ich zuvor noch nicht mit KASAN gearbeitet hatte, war ich unsicher, wie komplex die Nutzung ist. Daher entschied ich mich zunächst für die ersten beiden Optionen.
![]()
Linux3
Code, Code, Code – das Trainingsmodul war schnell erstellt.
Noch mehr Code – und auch ein einfacher zweithreadiger PoC war fertig.
Ein Blick auf ./poc && dmesg bestätigte die Vermutung: Die Debug-Ausgaben zeigten, dass sich der Bug tatsächlich relativ zuverlässig auslösen ließ.
![]()
Linux4
Eine weitere Iteration mit zusätzlichen Debug-Ausgaben im Zielmodul, Anpassung des PoC – und schließlich wurde klar, was passierte: printk() gab Kernel-Heap-Metadaten aus.
Was war also beim ersten Versuch schiefgelaufen?
Ganz simpel: Ich hatte einen entscheidenden Codeabschnitt übersehen, der die maximale Lesemenge begrenzte. Zwar konnte weiterhin über das Buffer-Ende hinaus gelesen werden, jedoch nicht in einem Umfang, der einen Kernel-Crash auslöste.
EIN BESSERER ANSATZ
Linux5
Warum also KASAN im Titel?
Ganz einfach: Nach all dem Aufwand entschied ich mich doch, KASAN auszuprobieren – und stellte fest, dass es mir enorm viel Zeit hätte sparen können. Statt mehrerer Tage war das Setup in ein bis zwei Stunden erledigt. Zusätzlich liefert KASAN eine klare, binäre Aussage: Ist das Verhalten eine Schwachstelle – ja oder nein?
KASAN arbeitet mit sogenanntem Shadow Memory, um Speicherzugriffe zu überwachen. Dabei repräsentiert ein Byte im Shadow Memory jeweils acht Byte echten Speichers. Werte zwischen 0x00 und 0x07 markieren gültigen Zugriff, während negative Werte spezielle Zustände kennzeichnen, z. B.:
-
0xff → bereits freigegebener Speicher
-
0xfc → Redzone zwischen Allokationen
Bei jedem Speicherzugriff prüft KASAN, ob sich der Zugriff innerhalb gültiger Speicherbereiche bewegt. [besthub.dev]
![]()
KASAN einrichten
Um KASAN zu nutzen, muss der Kernel neu kompiliert werden. Stellen Sie sicher, dass etwa 40 GB freier Speicherplatz verfügbar sind. Im folgenden Beispiel erfolgt der Build für ARM64 – bei Bedarf kann ARCH=arm64 weggelassen werden.
Abhängigkeiten installieren
$ sudo apt update
$ sudo apt install build-essential libncurses-dev bison flex libssl
Kernel-Quellcode herunterladen
$ apt source linux-image-$(uname -r)
$ cd linux-*
KASAN aktivieren
$ make ARCH=arm64 menuconfig
Navigation:
Kernel hacking → Memory Debugging → KASAN: runtime memory debugger
Wählen Sie den passenden KASAN-Modus (z. B. Generic KASAN).
Kernel kompilieren
$ make ARCH=arm64 -j $(nproc)
$ make ARCH=arm64 modules
$ make ARCH=arm64 dtbs
Beim Build trat in meinem Fall folgender Fehler auf:
LD [M] net/hsr/hsr.ko
LD [M] net/qrtr/qrtr.ko
LD [M] net/qrtr/qrtr-smd.ko
LD [M] net/qrtr/qrtr-mhi.ko
make: *** [Makefile:246: __sub-make] Error 2
Also habe ich den Build fortgesetzt, wobei ich nur eine Aufgabe hatte: eine bessere Fehlerausgabe zu erzielen.
$ make ARCH=arm64 -j 1
SYNC include/generated/autoconf.h
CALL scripts/checksyscalls.sh
DESCEND bpf/resolve_btfids
INSTALL libsubcmd_headers
UPD include/generated/utsversion.h
CC init/version-timestamp.o
BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
make[2]: *** [scripts/Makefile.vmlinux:37: vmlinux] Error 1
make[1]: *** [/media/sl4v/a6417fe8-1e5e-4957-86d7-0413e50d4dc6/kasan/linux-6.6.15/Makefile:1176: vmlinux] Error 2
make: *** [Makefile:246: __sub-make] Error 2
Glücklicherweise gab mir die Fehlermeldung ganz klar vor, was zu tun war: Ich sollte in die Datei .config gehen und CONFIG_DEBUG_INFO_BTF auf „n“ setzen – und es hat funktioniert. Ich habe den Kompilierungsprozess fortgesetzt, und der Rest verlief reibungslos.
Es folgt die Installation der Module und des Kernels selbst:
$ sudo make ARCH=arm64 modules_install
$ sudo make ARCH=arm64 install
Zum Schluss: Aktualisieren Sie die Bootloader-Konfiguration und starten das System neu:
$ sudo update-grub
$ sudo reboot
Nach dem Boot können Sie das Zielmodul laden und den PoC ausführen. KASAN liefert anschließend detaillierte Diagnosedaten inklusive Shadow-Memory-Dump.
Im untersuchten Fall zeigte der Dump, dass der fehlerhafte Zugriff auf eine Adresse mit Shadow-Wert 0xfc erfolgte – einer Redzone zwischen Puffern. Damit war klar: Das Modul liest über die Grenze des allokierten Speichers hinaus.
![]()
KASAN liefert nicht nur eine klare Aussage darüber, ob eine Schwachstelle vorliegt, sondern unterstützt auch effektiv bei der Root-Cause-Analyse – insbesondere in Kombination mit Fuzzing oder komplexen Race Conditions.
Für tiefergehende Einblicke in unsere Ansätze zur Entwicklung robuster Sicherheitsarchitekturen empfiehlt sich ein Blick auf unsere Cybersecurity-Services.