
Инструмент для эксплуатации переполнения при обработке ошибок ICMP в EDK II UEFI PXE (CVE-2026-97631): формирует поддельные ICMP-пакеты, внедряет подложный EFI_EVENT и выгружает память DXE через UART.
Инструментарий для переполнения при обработке ошибок ICMP в драйвере EDK II PXE Base Code
(NetworkPkg/UefiPxeBcDxe, GHSA-99cj-7hg9-mp4m, CVSS 4.0 9.5).
Во время PXE-загрузки драйвер копирует сообщение об ошибке ICMP в буфер фиксированного размера
504 байта. Более длинное сообщение превращается в линейную запись за пределы этого буфера,
в пул DXE. Поскольку адрес назначения записи совпадает с адресом самого буфера, полезная
нагрузка не зависит от позиции: байт i датаграммы попадает по адресу &Mode->IcmpError + i.
Переполнение внедряет поддельный EFI_EVENT; ядро DXE диспетчеризует его
через обычный путь CoreSignalEvent; полезная нагрузка восстанавливает событие из
снимка, обходит выбранные оператором диапазоны памяти и возвращает EFI_SUCCESS, чтобы
загрузка продолжилась.
Радиус поражения остаётся внутри собственной памяти DXE прошивки. Ни записи во flash, ни механизма персистентности, ни артефактов ОС.
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
Полезная нагрузка:
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
Результат — 378 байт, позиционно-независимый, без релокаций.
Отправитель и получатель используют POSIX (AF_PACKET, IP_HDRINCL, termios) и собираются
с помощью make в Linux. Симулятор собирается с MSVC через 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 отображает поддельную страницу пула, записывает переполнение туда, где это сделал бы уязвимый
memcpy, проверяет, что полученный IEVENT отправит полезную нагрузку, вызывает
полезную нагрузку как реальный машинный код, а затем проверяет:
26 проверок по компоновке, ABI, кадрированию и построению пакетов. Это доказывает, что части согласуются друг с другом. Это не доказывает адрес пула конкретной прошивки, который является выделением во время выполнения.
Два режима, используемые по порядку.
Probe: только легальный путь, без записи за границы.
pxeinj --target 10.0.0.50 --spoof 10.0.0.1 --sniff --probe --send
Корректно сформированная ICMP-ошибка размером 504 байта. Тот же спуфинг, та же встроенная BOOTP-транзакция, всё то же самое. Если клиент продолжает загрузку, значит ошибка была принята и достигнут уязвимый цикл копирования. Неразрушающее подтверждение, и ему не нужен адрес пула.
Overflow: отказывается собираться, пока не существуют три вещи: база пула
(tools/fw_offsets.py), исходные байты события (--shadow) и хотя бы один
диапазон.
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 — по одному START-END на строку, в hex без 0x; см.
ranges.example.txt для оценки размера и времени. Путь переполнения для v6
отключён: probe работает через v6, запись — нет, пока граница копирования для v6
не будет заново выведена из пропатченного исходного кода.
EFI_SUCCESS на нормальном пути notify, поэтому загрузка продолжаетсяSignalLink события самосогласован, поэтому обход списка не вызывает assertОстаточные следы присущи самому багу: событие было просигнализировано в момент, когда это обычно не происходит, страница пула остаётся «грязной», а если платформа измеряет DXE через TPM, значение PCR будет таким, каким оно было на момент начала загрузки. Всё сверх этого не является свойством данного бага.
--send обязателен; по умолчанию выполняется пробный запуск--pool-base, --payload, --shadow
и непустого списка диапазонов. Каждый из них имеет свой специфический способ
повредить прошивку при угадывании, поэтому ни один не является опциональным--shadow должен содержать сигнатуру EFI_EVENT по ожидаемому смещению, что
ловит и файл, заполненный нулями, и неверный --event-off одной проверкой,
а не в момент диспетчеризацииMode->IcmpError — это выделение времени выполнения, и он
различается для разных сборок прошивки. Его нельзя вывести из карты компоновки.
tools/fw_offsets.py даёт смещения; базу нужно измерить на целевой прошивке,
например из трассировки DEBUG_POOL, и затем закэшировать.sizeof(EFI_PXE_BASE_CODE_MODE) и offsetof(..., IcmpError) меняются между
релизами edk2. Значения по умолчанию в tools/fw_offsets.py — отправные точки,
а не значения, которым можно доверять.DpcHandle в целевом дереве, прежде чем полагаться на смещение переполнения.CoreSignalEvent будет
выполнен обход. По этой причине инструмент отказывается собираться без --shadow.