← ALL ADVISORIES

LOW BYTERAY-2026-0284Disclosed

EDK II - Out-of-Bounds Read in PxeBcSelectBootMenu

PxeBcSelectBootMenu iterates over variable-length PXEBC_BOOT_MENU_ENTRY structures from the DHCP4 vendor option 43 BOOT_MENU sub-option (tag 9) using a loop guarded only by MenuSize > 0 (line 314), which only proves at least 1 byte remains — insufficient for the 3-byte struct header (UINT16 Type + UINT8 DescLen).

Advisory
BYTERAY-2026-0284
CWE
CWE-125
Vendor
TianoCore
Product
EDK II
Reported
2026-06-25
Disclosure
2026-10-29
Affected
EDK II at commit 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21); affected code in NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c.
Credit
Argus-Systems

Executive Summary

PxeBcSelectBootMenu iterates over variable-length PXEBC_BOOT_MENU_ENTRY structures from the DHCP4 vendor option 43 BOOT_MENU sub-option (tag 9) using a loop guarded only by MenuSize > 0 (line 314), which only proves at least 1 byte remains — insufficient for the 3-byte struct header (UINT16 Type + UINT8 DescLen). The loop body reads MenuItem->DescLen (1 byte at offset +2) and MenuArray[0]->Type (2 bytes at offset +0) without verifying the full header fits. The size calculation MenuSize = (UINT8)(MenuSize - (MenuItem->DescLen + 3)) at line 317 can UINT8-underflow when DescLen+3 > MenuSize, wrapping to a large value (e.g., ~252) and keeping the loop alive past the boot-menu data boundary. This wrap at line 317 is the root defect. The walk is capped by PXEBC_MENU_MAX_NUM (24 entries, PxeBcImpl.h:70), each at most 258 bytes, and starts inside the cached packet in PXEBC_PRIVATE_DATA (Private->ProxyOffer or Private->DhcpAck, line 288). The variable-length DescStr array is then read at line 333 via PxeBcDisplayBootItem using the attacker-controlled DescLen with no bounds check, printing the over-read bytes to the local console.

This advisory is one of a set reported to VulnCheck on 2026-06-25 for coordinated disclosure to the EDK II maintainers. After several follow-ups over the following months no response was received. 126 days after the initial report, ByteRay is publishing the findings in good faith so that affected integrators can assess and remediate them. All findings were identified against EDK II commit 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21).

Affected Products and Versions

  • Vendor: TianoCore
  • Product: EDK II (NetworkPkg)
  • Affected commit: 70741a15d9a714323a5598eac47727e60507e22f (master, 2026-06-21). The code is present on that commit and later revisions that have not changed it.
  • Location: NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c:317 (loop at line 314)
  • Function: PxeBcSelectBootMenu
  • Category: CWE-125 Out-of-bounds Read

Technical Details

Controllability: EXTERNAL_UNTRUSTED — confidence: high

Data Flow

The vulnerability trace begins with a rogue DHCP4 server sending a crafted OFFER/ACK packet containing vendor option 43 with a BOOT_MENU sub-option (tag 9). PxeBcDhcp4CallBack (PxeBcDhcp4.c:1174) receives the packet via the DHCP4 protocol on a Dhcp4RcvdOffer/Dhcp4RcvdAck event. PxeBcParseVendorOptions (PxeBcDhcp4.c:148–151) extracts VendorOption->BootMenu and BootMenuLen directly from the option 43 BOOT_MENU sub-option data with no validation — both values are externally untrusted. PxeBcDiscoverBootFile (PxeBcBoot.c:906–933) calls PxeBcSelectBootMenu during PXE boot. The loop guard at PxeBcBoot.c:314 checks only MenuSize > 0 (at least 1 byte), not the 3-byte struct header. The sinks are: line 317 reads MenuItem->DescLen (offset +2) and performs a UINT8 subtraction that can underflow when DescLen+3 > MenuSize, wrapping to ~252 and keeping the loop alive past the boundary; line 324 reads MenuArray[0]->Type (2 bytes) without a full-fit check; line 333 calls PxeBcDisplayBootItem(MenuArray[Index]->DescStr, MenuArray[Index]->DescLen), reading DescLen bytes from past the buffer into adjacent struct fields or heap, displaying them on the local console.

Affected Callers

  • PxeBcDiscoverBootFile

Sanitization / Security Controls

  • MenuSize == 0 early return (PxeBcBoot.c:310–312) — ineffective: only checks for empty menu, does not validate struct header sizes or DescLen bounds.
  • PXEBC_MENU_MAX_NUM cap on Index (PxeBcBoot.c:314) — partial: limits the walk to 24 entries (at most about 24 * 258 bytes) but does not prevent OOB reads within a single entry.
  • Optional EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL user callback — bypassable: skipped if Callback==NULL; cannot validate internal struct field bounds regardless.
  • No sanitization points exist on the data path from network input to vulnerable sinks.

Reachability and Preconditions

Attacker position and trust level for this issue are stated in the Controllability line and Data Flow above, and the concrete trigger is in Reproduction below.

Exploitability: Medium
Realism: Low

Reproduction

  1. Attacker has access to the local network segment where PXE boot occurs; target machine has PXE boot enabled; no DHCP snooping or 802.1X port authentication.
  2. Set up rogue DHCP4 server.
  3. Respond to target's DHCP DISCOVER with crafted OFFER containing option 43 BOOT_MENU sub-option.
  4. Craft entry with DescLen+3 > MenuSize to trigger UINT8 underflow, or position entry at end of buffer so 3-byte header straddles boundary.
  5. Firmware caches options and victim ACKs.
  6. PxeBcSelectBootMenu iterates with underflowed MenuSize, reading past the buffer.
  7. PxeBcDisplayBootItem reads DescLen bytes of OOB data and prints them on the local console.

Impact

Bounded OOB read (at most about 24 * 258 bytes from the cached packet in PXEBC_PRIVATE_DATA) by an unauthenticated on-link rogue DHCP or proxyDHCP server. The over-read bytes are only printed as boot menu text on the local console and used to pick the boot item Type; nothing is sent back to the network attacker, so there is no remote information disclosure. A crash is possible only if the walk reaches unmapped memory.

Remediation and Mitigation

As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Before reading each field, validate that its full width fits within the remaining bytes derived from the packet or record size, and stop parsing or reject the input when it does not. The specific control that is absent or ineffective is identified in the Sanitization / Security Controls list under Technical Details.

Timeline

  • 2026-06-25 - Reported to VulnCheck for coordinated disclosure to the TianoCore / EDK II security team.
  • 2026-07 to 2026-10 - Several follow-ups sent; no response received.
  • 2026-10-29 - Public disclosure in good faith, 126 days after the initial report.

Credits

Discovered and reported by Argus-Systems.

References