← ALL ADVISORIES

MEDIUM BYTERAY-2026-0221Disclosed

EDK II - Buffer Over-Read in IScsiBuildKeyValueList

IScsiBuildKeyValueList receives an attacker-controlled iSCSI Login Response data segment (Data/Len) and, after locating the '=' delimiter for each key, calls AsciiStrLen(KeyValuePair->Value) at line 1925 to measure the value string length with no upper bound derived from the remaining Len.

Advisory
BYTERAY-2026-0221
CWE
CWE-126
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/IScsiDxe/IScsiProto.c.
Credit
Argus-Systems

Executive Summary

IScsiBuildKeyValueList receives an attacker-controlled iSCSI Login Response data segment (Data/Len) and, after locating the '=' delimiter for each key, calls AsciiStrLen(KeyValuePair->Value) at line 1925 to measure the value string length with no upper bound derived from the remaining Len. AsciiStrLen scans byte-by-byte for a NUL terminator that a malicious iSCSI target can omit from the last value in a crafted Login Response. The KEY search is properly bounded by the while loop at line 1903 (while ((Len > 0) && (*Data != '='))), but the VALUE length measurement via AsciiStrLen is completely unbounded. The subsequent SafeUint32Sub(Len, Result, &Len) sanity check at line 1932 uses the already-over-read-derived Result value (AsciiStrLen + 1), so it executes after the over-read already occurred and cannot retroactively prevent it. The data originates from network-received iSCSI PDUs via NetbufQueCopy into a heap buffer (AllocatePool in IScsiCheckOpParams — uninitialized memory; AllocateZeroPool in IScsiCHAPOnRspReceived — but AsciiStrLen still reads past the Len-byte buffer). A malicious or man-in-the-middle iSCSI target reached during network boot can trigger this.

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/IScsiDxe/IScsiProto.c:1925
  • Function: IScsiBuildKeyValueList
  • Category: CWE-126 (Buffer Over-read via unbounded string length on network data)

Technical Details

Controllability: EXTERNAL_UNTRUSTED — confidence: high

Data Flow

The data flow begins with a remote iSCSI target sending a Login Response PDU into Conn->RspQue. In IScsiCheckOpParams (IScsiProto.c:1521-1527), Len = Conn->RspQue.BufSize is set from the network PDU size, Data = AllocatePool(Len) allocates an uninitialized heap buffer, and NetbufQueCopy(&Conn->RspQue, 0, Len, (UINT8 *)Data) copies the network data to the heap. The untrusted data is then passed to IScsiBuildKeyValueList(Data, Len) at line 1534. Inside the parser, the KEY search at line 1903 (while ((Len > 0) && (*Data != '='))) is properly bounded by Len. After locating the '=' delimiter, KeyValuePair->Value = Data at line 1923 sets the value pointer into network data. At line 1925, Status = SafeUint32Add((UINT32)AsciiStrLen(KeyValuePair->Value), 1, &Result) is the vulnerable sink: AsciiStrLen on the Value has no upper bound derived from Len and over-reads heap past the buffer if the value lacks a NUL terminator. The SafeUint32Sub(Len, Result, &Len) sanity check at line 1932 uses the already-over-read-derived Result and executes after the over-read — it cannot prevent it. The same pattern exists in IScsiCHAPOnRspReceived (IScsiCHAP.c:241-255) with AllocateZeroPool. IScsiUpdateTargetAddress (IScsiProto.c:927) receives data via NetbufGetByte from the network PDU in IScsiProcessLoginRsp.

Affected Callers

  • IScsiCheckOpParams
  • IScsiCHAPOnRspReceived
  • IScsiUpdateTargetAddress

Sanitization / Security Controls

  • Bounds check at IScsiProto.c:1932 (SafeUint32Sub(Len, Result, &Len)): ineffective — uses Result (derived from AsciiStrLen + 1) which was computed by the over-read at line 1925; executes after the over-read already occurred. When the NUL lies past the buffer, Result exceeds Len, the check fails and the whole list is discarded, so the over-read bytes are not used further.
  • Bounds check at IScsiProto.c:1903 (while ((Len > 0) && (*Data != '=')) key search loop): effective for key only — bounds the KEY portion but does not bound the subsequent VALUE length measurement via AsciiStrLen at line 1925.
  • Opcode check at IScsiProto.c:888 (ISCSI_CHECK_OPCODE(LoginRsp, ISCSI_OPCODE_LOGIN_RSP)): ineffective — only validates the PDU opcode type, not the data segment contents; a valid opcode with a malicious data segment passes this check.

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: Medium

Reproduction

  1. Firmware iSCSI initiator opens TCP connection to target and sends Login Request.
  2. Attacker (malicious target or MITM) responds with a crafted Login Response PDU.
  3. The crafted PDU's data segment contains key=value pairs where the last value omits the NUL terminator and the buffer is filled with non-NUL bytes up to Len.
  4. IScsiCheckOpParams / IScsiCHAPOnRspReceived / IScsiProcessLoginRsp copies the data segment from Conn->RspQue (or via NetbufGetByte) into a heap buffer and calls IScsiBuildKeyValueList.
  5. The bounded key search loop (line 1903) correctly stops at the '=' delimiter, but when AsciiStrLen measures the value length (line 1925), it scans past the Len-byte buffer into adjacent heap memory.
  6. The scan stops at the first zero byte after the buffer, in pool padding or the pool tail; with heap guard enabled and the buffer ending at a guard page, the read faults.
  7. The SafeUint32Sub check at line 1932 fires after the over-read and fails, so IScsiBuildKeyValueList returns NULL and the login fails.

Impact

Out-of-bounds read (CWE-126) of DXE pool memory past the end of the data segment buffer, triggered by an unauthenticated iSCSI target (or a TCP man-in-the-middle) during login. The scan continues until it reaches a zero byte in the adjacent pool padding or pool tail, so it normally runs only a few bytes past the buffer. Nothing read is disclosed: the over-read length only feeds the SafeUint32Sub check at line 1932, which then fails and discards the list. A crash is possible only when the buffer ends at a guard page or unmapped memory (for example with heap guard enabled), resulting in a denial of service during network boot.

Remediation and Mitigation

As of commit 70741a15d9a714323a5598eac47727e60507e22f this issue is unfixed upstream and no CVE has been assigned. Bound the string scan by the remaining buffer length instead of relying on a terminator the attacker can omit. 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