By Manny Fernandez

November 7, 2026

CVE-2026-97631: EDK2 PXE ICMP Overflow Turns Network Boot into Pre-Boot RCE

CVE-2026-97631: EDK2 PXE ICMP Overflow Turns Network Boot into Pre-Boot RCE

InfoSecMonkey | October 29, 2026 | No fluff. Just the config that works.

Executive Summary

The EDK2 PXE ICMP overflow (CVE-2026-97631, CVSS 4.0 score 9.5, Critical) lets an unauthenticated attacker run code inside UEFI firmware while a machine is network booting. One oversized ICMP error message, sent to the client’s IP during the PXE window, overflows a fixed 504-byte buffer in the EDK II PXE Base Code driver. The result is either a hung boot or attacker code running in the DXE phase at ring 0, before Secure Boot or measured boot has verified a single byte of the operating system.

EDK II (TianoCore) is the open source UEFI reference code that most commercial firmware is built on, so this is not one vendor’s bug. It lands in servers, workstations, thin clients and virtual machines that use OVMF.

Item Detail
CVE CVE-2026-97631
Severity Critical, CVSS 4.0 base score 9.5
Vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness CWE-120 classic buffer overflow, CWE-131 incorrect buffer size calculation, CWE-787 out-of-bounds write
Component NetworkPkg/UefiPxeBcDxe (PXE Base Code driver)
Vulnerable functions PxeBcIcmpErrorDpcHandle (IPv4) and PxeBcIcmp6ErrorDpcHandle (IPv6)
Affected EDK II master as of 2026-09-14 and downstream firmware built from it
Upstream fix PR #13302, merged to master October 15, 2026 (commit fa78907)
Credit Alex Matrosov (Anthropic) and Dan Holmes-Mitra (Google)

Objective: explain how the bug works, find out where you are exposed, and put patching, isolation and detection in place on your provisioning networks with FortiGate.

Target audience: network and security engineers, data center and VDI teams that run PXE, and anyone who owns firmware patching for a server fleet.

How the EDK2 PXE ICMP Overflow Works

Where it lives in the boot chain

UEFI boots in phases: SEC, PEI, DXE, then BDS hands off to a boot loader. The PXE Base Code driver runs in DXE. At that point the firmware owns the CPU, nothing from your OS exists yet, and the Secure Boot checks that protect you later have not started on the OS loader. That is why a firmware network bug is worth more to an attacker than almost any OS bug: there is no EDR, no kernel protection, and no log.

The bug

During a network boot the PXE driver keeps a receive request open for ICMP error messages (destination unreachable, time exceeded and so on) so it can report them to the boot loader. Each received error is copied into Mode.IcmpError, a fixed 504-byte field inside the driver’s private context structure. The copy loop gets two things wrong, in both the IPv4 and IPv6 handlers:

  • It keeps copying bytes into the field when the message is longer than 504 bytes instead of stopping or truncating.
  • It advances the destination pointer by the cumulative length copied so far instead of the length of the fragment it just copied, so later fragments land further and further past the end of the field.
  • Nothing earlier in the receive path limits the size of the incoming ICMP message, so the attacker controls how far the write goes and what it writes.

The logic looks roughly like this. This is an illustrative sketch built from the public description, not the actual EDK II source:

// ILLUSTRATIVE ONLY: simplified from the published root cause
UINT8  *Dst   = (UINT8 *)&Mode->IcmpError;   // 504-byte field
UINT32 Copied = 0;

for (Index = 0; Index < RxData->FragmentCount; Index++) {
  Len     = RxData->FragmentTable[Index].FragmentLength;
  Copied += Len;                              // running total
  // Bug 1: no hard stop once Copied passes 504 bytes
  CopyMem (Dst, RxData->FragmentTable[Index].FragmentBuffer, Len);
  Dst    += Copied;                           // Bug 2: should be += Len
}

The upstream fix does what you would expect: zero the buffer first, copy at most the space that remains, advance by the per-fragment length, and truncate anything that does not fit.

// ILLUSTRATIVE ONLY: the shape of the fix
ZeroMem (&Mode->IcmpError, sizeof (Mode->IcmpError));
Remaining = sizeof (Mode->IcmpError);

for (Index = 0; Index < RxData->FragmentCount; Index++) {
  if (Remaining == 0) {
    break;                                    // truncate oversized errors
  }
  Len = MIN (RxData->FragmentTable[Index].FragmentLength, Remaining);
  CopyMem (Dst, RxData->FragmentTable[Index].FragmentBuffer, Len);
  Dst       += Len;
  Remaining -= Len;
}

What an attacker gets

  • Denial of service, every time. The overflow corrupts the driver’s private data, and the next ICMP error triggers a CPU exception inside the DXE core. The boot hangs, which breaks OS provisioning and recovery for whatever is being imaged.
  • Code execution, with homework. An attacker who knows the target firmware build can place a counterfeit event structure in the same packet. When the firmware calls CoreSignalEvent() on it, the attacker’s bytes are dispatched at ring 0 in DXE. That build knowledge is why the vector says Attack Requirements: Present.
  • No credentials, no user, any distance. The attacker only needs to land a unicast ICMP error on the client’s IP. If that IP is reachable from where the attacker sits, distance does not matter.

The exposure window is the PXE boot itself. A laptop that PXE boots once a year is a small target. A server whose boot order tries the network before the disk opens that window on every single reboot, patch night included.

If this sounds familiar, it should. NetworkPkg was the same code behind the PixieFail set of bugs in early 2024. Pre-boot network stacks are small, old and rarely fuzzed, and they parse attacker-reachable packets before anything else on the box is awake.

Prerequisites and Architecture

Assumed knowledge: UEFI boot order basics, PXE (DHCP plus TFTP or HTTP), FortiGate interfaces and firewall policies, and reading tcpdump filters.

Lab requirements: a FortiGate running FortiOS 7.4 or later, a provisioning VLAN, a Linux host for packet capture, and one test host outside the VLAN. Never test against production machines that are mid-boot: a vulnerable client will hang.

Component Role Lab address
FortiGate Gateway and policy point for the provisioning VLAN 10.0.50.1 (VLAN 50)
Provisioning VLAN Where PXE clients boot 10.0.50.0/24
Boot server DHCP, TFTP or HTTP boot images 10.0.50.10
PXE client Server or VM being imaged 10.0.50.20
Capture host Linux box on a mirror port or the boot server 10.0.50.11
External test host Simulates an off-segment sender 198.18.10.50

Figure 1: Attack path and control points for CVE-2026-97631

Attack path for CVE-2026-97631: oversized ICMP error overflows the EDK2 PXE driver buffer, with four control points

Step-by-Step: Find, Patch and Contain

Step 1: Find the machines that network boot

Goal: a list of hosts that attempt PXE, and how often.

Action: read the UEFI boot entries from the running OS. Anything with a network entry ahead of the disk in BootOrder tries PXE on every reboot.

# Linux: list UEFI boot entries and the boot order
sudo efibootmgr -v | grep -iE 'BootOrder|pxe|ipv4|ipv6|network|nic'

# Windows (elevated): firmware boot manager entries
bcdedit /enum firmware

GUI verification: open the BMC (iDRAC, iLO, XClarity or similar) or the firmware setup screen and check the boot sequence. Note whether IPv4 and IPv6 network boot are both enabled on each NIC.

Step 2: Identify the firmware and check for a vendor fix

Goal: know which firmware build each host runs and whether your OEM has shipped one with the fix.

Action: the upstream fix only protects you once your platform vendor rebuilds firmware on top of it. Pull the version, then check the OEM and the BIOS vendor (AMI, Insyde, Phoenix and others) for an advisory that names CVE-2026-97631.

# Current BIOS/UEFI vendor, version and date
sudo dmidecode -s bios-vendor
sudo dmidecode -s bios-version
sudo dmidecode -s bios-release-date

# Hardware that publishes through LVFS
sudo fwupdmgr refresh
sudo fwupdmgr get-updates

GUI verification: in the BMC firmware inventory, confirm the BIOS version after the update and that the host completed a full power cycle.

Step 3: Patch virtual firmware (OVMF)

Goal: VMs that boot with OVMF get a fixed EDK II build.

Action: check the package on every hypervisor. At the time of writing, the Debian security tracker for CVE-2026-97631 still lists the edk2 source package as vulnerable in bookworm, trixie and sid, so watch your distribution for the update.

# Debian / Ubuntu hosts
apt policy ovmf

# RHEL / Rocky / Alma hosts
rpm -q edk2-ovmf

# Proxmox VE
pveversion -v | grep -i edk2

GUI verification: after the update, power the VM fully off and start it again from the hypervisor console. A reboot from inside the guest keeps the old firmware loaded (see Troubleshooting).

Step 4: Shrink the exposure window

Goal: hosts only PXE boot when someone actually wants them to.

Action: move network boot below local disk in the default order, disable the IP stack you do not use for network boot, and use a one-time boot override when you need to reimage.

# Put the disk entry first (example entry numbers, use your own)
sudo efibootmgr -o 0001,0000

# Request PXE for the next boot only through the BMC
ipmitool -I lanplus -H <BMC_IP> -U <USER> -P '<PASS>' chassis bootdev pxe
ipmitool -I lanplus -H <BMC_IP> -U <USER> -P '<PASS>' power cycle

Both the IPv4 and IPv6 handlers are vulnerable, so turning one off reduces exposure but does not remove it. If you are moving to UEFI HTTP boot, confirm with your firmware vendor that the build you run is patched, rather than assuming a different boot protocol means different code.

GUI verification: the BMC boot settings show local disk first and the one-time override cleared after the next boot.

Step 5: Isolate the provisioning VLAN on FortiGate

Goal: nobody outside the provisioning VLAN can deliver an ICMP message to a booting client.

Action: give provisioning its own VLAN, keep the boot server inside it, and deny ICMP from every other interface into it. PXE clients do not need ICMP errors from the outside world to boot.

config system interface
    edit "PXE-VLAN50"
        set vdom "root"
        set ip 10.0.50.1 255.255.255.0
        set allowaccess ping
        set role lan
        set interface "<PARENT_INTF>"
        set vlanid 50
    next
end

config firewall address
    edit "NET-PXE-10.0.50.0"
        set subnet 10.0.50.0 255.255.255.0
    next
end
config firewall policy
    edit 0
        set name "DENY-ICMP-INTO-PXE"
        set srcintf "<WAN_INTF>" "<CORE_INTF>"
        set dstintf "PXE-VLAN50"
        set srcaddr "all"
        set dstaddr "NET-PXE-10.0.50.0"
        set action deny
        set schedule "always"
        set service "ALL_ICMP"
        set logtraffic all
    next
    edit 0
        set name "DENY-ICMP6-INTO-PXE"
        set srcintf "<WAN_INTF>" "<CORE_INTF>"
        set dstintf "PXE-VLAN50"
        set srcaddr6 "all"
        set dstaddr6 "<PXE_NET6_OBJECT>"
        set action deny
        set schedule "always"
        set service "ALL_ICMP6"
        set logtraffic all
    next
end

Policies created with edit 0 land at the bottom of the list. Move both above any policy that permits traffic into the VLAN:

config firewall policy
    move <DENY_ICMP_ID> before <FIRST_ALLOW_INTO_PXE_ID>
    move <DENY_ICMP6_ID> before <FIRST_ALLOW_INTO_PXE_ID>
end

Then make sure the VLAN has no outbound path to the Internet or to untrusted networks. Pull boot images from a server inside the VLAN.

GUI verification: Policy and Objects, Firewall Policy. Both deny policies sit above the allow rules for PXE-VLAN50, and Log Allowed Traffic is set to All Sessions so hits are visible.

Step 6: Close the same-segment path

Goal: an attacker plugged into the provisioning VLAN cannot reach booting clients either.

Action: traffic between two hosts in 10.0.50.0/24 never touches the FortiGate, so the firewall policy above cannot see it. Lock down access ports on the provisioning VLAN with 802.1X or MAC authentication bypass through your NAC, keep user and IoT devices off it entirely, and consider a separate provisioning VLAN per build rack so one compromised host cannot reach every client being imaged.

GUI verification: on the switch or FortiSwitch port view, every port in VLAN 50 shows an authenticated device or is shut down.

Step 7: Detect oversized ICMP errors

Goal: see an attempt even where you cannot block it.

Action: the trigger is an ICMP message longer than 504 bytes. For IPv4 without options that is an IP total length above 524. RFC 1812 caps ICMPv4 errors at 576 bytes, so anything bigger is out of spec, and anything between 525 and 576 aimed at a PXE client deserves a look. Fragmented ICMP into a provisioning network is also odd enough to alert on.

# IPv4 ICMP errors (types 3,4,5,11,12) longer than 504 bytes of ICMP
sudo tcpdump -ni <IFACE> 'icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5
  or icmp[0]=11 or icmp[0]=12) and ip[2:2] > 524'

# Any fragmented ICMP (MF set or non-zero offset)
sudo tcpdump -ni <IFACE> 'icmp and (ip[6:2] & 0x3fff != 0)'

# ICMPv6 error messages (types 0-127) over 504 bytes, no ext headers
sudo tcpdump -ni <IFACE> 'icmp6 and ip6[40] < 128 and ip6[4:2] > 504'

On the FortiGate itself, the same IPv4 filter works with the built-in sniffer:

diagnose sniffer packet PXE-VLAN50 'icmp and ip[2:2] > 524' 4 0 l

GUI verification: if you feed the capture into a SIEM, build an alert for any match where the destination is in a provisioning subnet.

Verification and Validation

Firewall check (lab only): from the external test host, send a few oversized ICMP destination-unreachable messages at an idle lab client. The FortiGate should drop and log them, and the capture on PXE-VLAN50 should stay empty.

# From 198.18.10.50: three type 3 code 1 messages with 600 data bytes
sudo hping3 --icmp -C 3 -K 1 -d 600 -c 3 10.0.50.20
execute log filter category traffic
execute log filter field policyid <DENY_ICMP_ID>
execute log display

Expected success output: deny entries from the external host, and nothing on the inside sniffer.

1: date=2026-10-29 time=11:42:07 type="traffic" subtype="forward"
   srcip=198.18.10.50 dstip=10.0.50.20 proto=1 action="deny"
   policyid=<DENY_ICMP_ID> policyname="DENY-ICMP-INTO-PXE"
   service="icmp" dstintf="PXE-VLAN50"

FGT # diagnose sniffer packet PXE-VLAN50 'icmp and ip[2:2] > 524' 4 0 l
interfaces=[PXE-VLAN50]
filters=[icmp and ip[2:2] > 524]
0 packets received by filter

Firmware check: after the vendor update and a full power cycle, sudo dmidecode -s bios-version returns the fixed build listed in the OEM advisory. For VMs, the hypervisor package version matches the fixed release from your distribution.

Troubleshooting and Gotchas

1. The hypervisor is patched but the VM is still vulnerable

OVMF is read when the QEMU process starts. A reboot from inside the guest reuses the same process and the old firmware image. Stop and start the VM, or live migrate it to a patched host, then confirm the process started after the package update:

# QEMU processes and their start time
ps -eo pid,lstart,cmd | grep -i [q]emu | grep -i ovmf

2. ICMP still reaches the VLAN despite the deny policy

A stateful firewall forwards ICMP errors that relate to a session it already allowed, because that is how path MTU discovery and unreachable messages are supposed to work. If PXE clients open sessions to anything outside the VLAN, such as a remote TFTP or HTTP boot server, an attacker on that path can ride the existing session. Keep the boot server inside the VLAN and check for outbound sessions:

diagnose sys session filter clear
diagnose sys session filter src 10.0.50.0 10.0.50.255
diagnose sys session list

3. Detection fires on legitimate IPv6 errors

RFC 4443 lets an ICMPv6 error carry as much of the original packet as fits in 1280 bytes, so normal IPv6 errors can exceed 504 bytes. Treat the IPv6 filter as alert and correlate, not block, and narrow it to destinations in the provisioning prefix. If you see a capture that shows nothing at all, also confirm you are on a port that sees the client traffic: two hosts on the same VLAN talk through the switch, not the FortiGate.

Bottom Line

  • Ask every server, workstation and BMC vendor for firmware that includes the CVE-2026-97631 fix, and update OVMF on your hypervisors.
  • Until then, network boot should be a one-time event, not a default boot order entry.
  • Provisioning VLANs get no Internet path, no inbound ICMP, and authenticated access ports.
  • Alert on oversized and fragmented ICMP errors aimed at provisioning subnets.

Sources

InfoSecMonkey: No fluff. Just the config that works.

Recent posts

  • If you've spent any time configuring user authentication on... Full Story

  • DNS is one of those technologies that quietly underpins... Full Story

  • BGP issues on FortiGate firewalls usually trace back to... Full Story

  • Every time your laptop talks to your router, a... Full Story

  • If you've spent any time configuring NAT on a... Full Story

  • If you have spent any time configuring firewall policies... Full Story

  • High availability on FortiGate is one of those features... Full Story

  • If you've configured SD-WAN on a FortiGate, you've almost... Full Story

  • FortiLink is the management protocol that turns a FortiSwitch... Full Story

  • FortiSwitches are pretty rock solid from Mean Time Between... Full Story

  • This is a quicky tip.  Have you ever gone... Full Story

  • DNS is one of those quiet pieces of internet... Full Story

  • This article is an updated version of the previous... Full Story

  • You will add ns2 as a secondary (slave) BIND9... Full Story

  • In the process of deploying my lab, I needed... Full Story

  • RFC 8805, used to be known as Self-Correcting IP... Full Story

  • Years back, I wrote an article about certificate pinning. ... Full Story

  • FortiGates have the ability to send alerts to Microsoft... Full Story

  • In this post, I am going to walk through... Full Story

  • Troubleshooting VoIP on a FortiGate can feel like trying... Full Story

  • Prior to FortiOS 7.0, there were three commands to... Full Story

  • In this post, I am going to go over... Full Story

  • What we are going to do:  We are going... Full Story

  • Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story

  • Creating a VLAN on macOS (The "Pro" Move) A... Full Story

  • This blog post explores the logic behind how macOS... Full Story

  • Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story

  • Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story

  • ICMP introduces several security risks, but careful filtering, rate... Full Story

  • The command diag debug application dhcps -1 enables full... Full Story

  • In the world of FortiOS, execute tac report is... Full Story

  • LLDP; What is it The Link Layer Discovery Protocol... Full Story

  • What it actually does When you run diagnose fdsm... Full Story

  • Monkey Bites are bite-sized, high-impact security insights designed for... Full Story

  • I have run macOS in macOS with Parallels but... Full Story

  • Don't be confused with my other FortiNAC posts where... Full Story

  • This is the third session in a multi-part article... Full Story

  • Today I was configuring key-based authentication on a FortiGate... Full Story

  • Netcat, often called the "Swiss Army knife" of networking,... Full Story

  • At its core, IEEE 802.1X is a network layer... Full Story

  • In case you did not see the previous FortiNAC... Full Story

  • This is our 5th session where we are going... Full Story

  • Now that we have Wireshark installed and somewhat configured,... Full Story

  • The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story

  • No fluff. Just the config that works. Executive Summary... Full Story

  • INFOSECMONKEY | CVE BRIEF | NO FLUFF. JUST THE... Full Story

  • APT CVE-2026-109518: How a Download Retry Lets an Attacker... Full Story