
Strumenti di exploit per l'overflow dell'errore ICMP di EDK II UEFI PXE (CVE-2026-97631): crea pacchetti ICMP falsificati, inserisce un EFI_EVENT contraffatto ed esegue il dump della memoria DXE tramite UART.
Strumenti per l'overflow degli errori ICMP del driver EDK II PXE Base Code
(NetworkPkg/UefiPxeBcDxe, GHSA-99cj-7hg9-mp4m, CVSS 4.0 9.5).
Durante un avvio PXE il driver copia un messaggio di errore ICMP in un buffer
fisso di 504 byte. Un messaggio più lungo diventa una scrittura lineare oltre quel buffer, nel pool
DXE. Poiché la destinazione della scrittura è l'indirizzo del buffer stesso, il payload è
indipendente dalla posizione: il byte i del datagramma finisce in &Mode->IcmpError + i.
L'overflow inserisce un EFI_EVENT contraffatto; il core DXE lo distribuisce
attraverso il normale percorso CoreSignalEvent; il payload ripristina l'evento da uno
snapshot, percorre intervalli di memoria scelti dall'operatore e restituisce EFI_SUCCESS affinché
l'avvio prosegua.
Il raggio d'azione resta all'interno della memoria DXE del firmware stesso. Nessuna scrittura su flash, nessun meccanismo di persistenza, nessun artefatto del sistema operativo.
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
Il risultato è di 378 byte, indipendente dalla posizione, senza rilocazioni.
Mittente e ricevitore sono POSIX (AF_PACKET, IP_HDRINCL, termios) e si compilano
con make su Linux. Il simulatore si compila con MSVC tramite Makefile.win:
cl /nologo /W3 /O2 /Iinclude /Fe:build\sim.exe sim\sim.c src\craft.c src\graft.c src\layout.c
build/sim.exe mappa una pagina di pool fittizia, scrive l'overflow dove lo farebbe la memcpy vulnerabile, verifica che l'IEVENT risultante invierebbe il payload, chiama il payload come vero codice macchina e poi verifica:
26 controlli su layout, ABI, framing e costruzione dei pacchetti. Questo dimostra che le parti sono coerenti tra loro. Non dimostra l'indirizzo di pool di un firmware specifico, che è un'allocazione a runtime.
Due modalità, usate in sequenza.
Probe: solo percorso legale, nessuna scrittura fuori dai limiti.
pxeinj --target 10.0.0.50 --spoof 10.0.0.1 --sniff --probe --send
Un errore ICMP ben formato da 504 byte. Stesso spoofing, stessa transazione BOOTP incorporata, tutto identico. Se il client continua l'avvio, l'errore è stato accettato e il ciclo di copia vulnerabile è stato raggiunto. Conferma non distruttiva, e non richiede alcun indirizzo del pool.
Overflow: rifiuta di essere costruito finché non esistono tre cose: la base del pool
(tools/fw_offsets.py), i byte dell'evento incontaminati (--shadow) e almeno un
range.
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 contiene un START-END per riga, in esadecimale senza 0x; vedere
ranges.example.txt per le dimensioni e il budget di tempo. Il percorso di overflow v6 è
disabilitato: la sonda funziona su v6, la scrittura no, finché il limite della copia v6 non
è stato ricalcolato dal sorgente corretto.
EFI_SUCCESS sul percorso di notify normale, quindi il boot procedeSignalLink dell'evento è autoconsistente, quindi la scansione della lista non genera un assertLe tracce residue sono intrinseche al bug: l'evento è stato segnalato in un momento in cui normalmente non lo sarebbe, la pagina del pool resta sporca, e se la piattaforma misura DXE con un TPM il valore PCR è quello che era all'avvio del boot. Qualsiasi cosa oltre a questo non è una proprietà di questo bug.
--send è obbligatorio; il comportamento predefinito è una dry run--pool-base, --payload, --shadow
e una lista di range non vuota. Ognuno di questi ha un modo specifico di corrompere
il firmware se indovinato, quindi nessuno è opzionale--shadow deve contenere una firma EFI_EVENT all'offset previsto, il che intercetta
un file riempito di zeri e un --event-off errato in un solo controllo invece che
al momento del dispatchMode->IcmpError è un'allocazione a runtime e differisce per
ogni build del firmware. Non può essere ricavato da una link map. tools/fw_offsets.py
fornisce gli offset; la base deve essere misurata sul firmware target, per
esempio da una traccia DEBUG_POOL, e poi memorizzata in cache.sizeof(EFI_PXE_BASE_CODE_MODE) e offsetof(..., IcmpError) cambiano tra
le release di edk2. I valori predefiniti in tools/fw_offsets.py sono punti di partenza, non
valori di cui fidarsi.DpcHandle
nell'albero target prima di fare affidamento sull'offset di overflow.CoreSignalEvent va a finire. Per questo motivo lo strumento rifiuta
di essere compilato senza --shadow.