
Exploit-Tooling für den EDK II UEFI PXE ICMP-Fehlerüberlauf (CVE-2026-97631): erstellt gefälschte ICMP-Pakete, platziert ein nachgeahmtes EFI_EVENT und gibt den DXE-Speicher über UART aus.
Tooling für den ICMP-Fehlerüberlauf des EDK II PXE Base Code-Treibers
(NetworkPkg/UefiPxeBcDxe, GHSA-99cj-7hg9-mp4m, CVSS 4.0 9.5).
Während eines PXE-Boots kopiert der Treiber eine ICMP-Fehlermeldung in einen festen 504-Byte-
Puffer. Eine längere Nachricht wird zu einem linearen Schreibvorgang über diesen Puffer hinaus in den DXE-
Pool. Da das Schreibziel die Adresse des Puffers selbst ist, ist die Nutzlast
positionsunabhängig: Byte i des Datagramms landet bei &Mode->IcmpError + i.
Der Überlauf platziert ein gefälschtes EFI_EVENT; der DXE-Kern leitet es
über den normalen CoreSignalEvent-Pfad weiter; die Nutzlast stellt das Ereignis aus einem
Snapshot wieder her, durchläuft vom Bediener gewählte Speicherbereiche und gibt EFI_SUCCESS zurück, damit der
Boot fortgesetzt wird.
Der Wirkungsbereich bleibt innerhalb des DXE-Speichers der Firmware selbst. Kein Flash-Schreibvorgang, kein Persistenzmechanismus, kein Betriebssystem-Artefakt.
include/pxe_layout.h EFI_PXE_BASE_CODE_ICMP_ERROR, IEVENT offsets, wire structs
include/craft.h one packet = one primitive
include/graft.h counterfeit event + payload parameter block
include/layout.h the overflow region (single source of truth)
src/payload.S ring-0 payload: restore, walk ranges, stream out
src/craft.c packet construction, IPv4 + IPv6
src/graft.c IEVENT overlay, parameter block
src/layout.c overflow assembly, shared by sender and simulator
src/main.c pxeinj: sniff xid, probe, send
src/net.c raw L3 transport, DHCP xid sniffer
src/recv.c pxerecv: serial collector, dump + map
sim/sim.c host harness that proves the chain
tools/fw_offsets.py pool offsets from a debug firmware build
Payload:
as --64 -o build/payload.o src/payload.S
ld -Ttext=0 -e payload_start --oformat elf64-x86-64 -o build/payload.elf build/payload.o
objcopy -O binary --only-section=.text build/payload.elf build/payload.bin
Ergebnis ist 378 Bytes, positionsunabhängig, ohne Relokationen.
Sender und Empfänger sind POSIX (AF_PACKET, IP_HDRINCL, termios) und lassen sich
mit make unter Linux bauen. Der Simulator lässt sich mit MSVC über Makefile.win bauen:
cl /nologo /W3 /O2 /Iinclude /Fe:build\sim.exe sim\sim.c src\craft.c src\graft.c src\layout.c
build/sim.exe mappt eine gefälschte Pool-Seite, schreibt den Overflow dorthin, wo das verwundbare
memcpy ihn schreiben würde, prüft, ob das resultierende IEVENT die Payload dispatchen würde, ruft
die Payload als echten Maschinencode auf und verifiziert anschließend:
26 Prüfungen über Layout, ABI, Framing und Paketkonstruktion. Dies beweist, dass die Teile zueinander passen. Es beweist nicht die Pool-Adresse einer bestimmten Firmware, die eine Laufzeit-Allokation ist.
Zwei Modi, in dieser Reihenfolge zu verwenden.
Probe: nur legaler Pfad, kein Out-of-Bounds-Schreiben.
pxeinj --target 10.0.0.50 --spoof 10.0.0.1 --sniff --probe --send
Ein wohlgeformter ICMP-Fehler mit 504 Byte. Gleicher Spoof, gleiche eingebettete BOOTP-Transaktion, alles identisch. Wenn der Client weiterhin bootet, wurde der Fehler akzeptiert und die verwundbare Kopierschleife erreicht. Zerstörungsfreie Bestätigung, und sie benötigt keine Pool-Adresse.
Overflow: weigert sich zu bauen, bis drei Dinge vorhanden sind: die Pool-Basis (tools/fw_offsets.py), die unveränderten Event-Bytes (--shadow) und mindestens ein Bereich.
pxeinj --target 10.0.0.50 --spoof 10.0.0.1 --xid 0xdeadbeef \
--pool-base 0x7f12345000 --payload build/payload.bin \
--ranges ranges.txt --shadow shadow.bin --uart 0x3f8 --send
pxerecv -d /dev/ttyUSB0 -o dump.bin -m dump.map
ranges.txt enthält eine START-END-Angabe pro Zeile, hexadezimal ohne 0x; siehe
ranges.example.txt für die Größen- und Zeitbudgetierung. Der v6-Overflow-Pfad ist
deaktiviert: Die Probe funktioniert über v6, der Schreibvorgang nicht, bis die v6-Kopiergrenze
aus dem gepatchten Quellcode neu abgeleitet wurde.
EFI_SUCCESS zurück, sodass der Boot fortgesetzt wirdSignalLink des Events ist selbstkonsistent, sodass der Listendurchlauf keine Assertion auslöstRestspuren sind dem Bug inhärent: Das Event wurde zu einem Zeitpunkt signalisiert, zu dem es normalerweise nicht signalisiert würde, die Pool-Seite bleibt schmutzig, und wenn die Plattform DXE mit einem TPM misst, ist der PCR-Wert derjenige, der beim Start des Bootvorgangs galt. Alles darüber hinaus ist keine Eigenschaft dieses Bugs.
--send ist erforderlich; standardmäßig wird ein Dry Run ausgeführt--pool-base, --payload, --shadow
und eine nicht leere Bereichsliste. Jede dieser Angaben kann die Firmware auf eine
bestimmte Weise beschädigen, wenn sie geraten wird, daher ist keine optional--shadow muss an der erwarteten Position eine EFI_EVENT-Signatur enthalten, was
eine mit Nullen gefüllte Datei und ein falsches --event-off in einer einzigen Prüfung
erkennt statt erst zur Dispatch-ZeitMode->IcmpError ist eine Laufzeitallokation und unterscheidet sich je
Firmware-Build. Sie kann nicht aus einer Link Map abgeleitet werden. tools/fw_offsets.py
liefert die Offsets; die Basis muss auf der Ziel-Firmware gemessen werden, zum
Beispiel aus einem DEBUG_POOL-Trace, und dann zwischengespeichert werden.sizeof(EFI_PXE_BASE_CODE_MODE) und offsetof(..., IcmpError) ändern sich zwischen
edk2-Releases. Die Standardwerte in tools/fw_offsets.py sind Ausgangspunkte, keine
Werte, denen man vertrauen sollte.DpcHandle-
Funktionen im Zielbaum, bevor du dich auf den Overflow-Offset verlässt.CoreSignalEvent hineinläuft. Aus diesem Grund verweigert das Tool
den Build ohne --shadow.