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
IScsiCheckOpParamsIScsiCHAPOnRspReceivedIScsiUpdateTargetAddress
Sanitization / Security Controls
- Bounds check at
IScsiProto.c:1932(SafeUint32Sub(Len, Result, &Len)): ineffective — usesResult(derived fromAsciiStrLen + 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,ResultexceedsLen, 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 viaAsciiStrLenat 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
- Firmware iSCSI initiator opens TCP connection to target and sends Login Request.
- Attacker (malicious target or MITM) responds with a crafted Login Response PDU.
- 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. IScsiCheckOpParams/IScsiCHAPOnRspReceived/IScsiProcessLoginRspcopies the data segment fromConn->RspQue(or viaNetbufGetByte) into a heap buffer and callsIScsiBuildKeyValueList.- The bounded key search loop (line 1903) correctly stops at the
'='delimiter, but whenAsciiStrLenmeasures the value length (line 1925), it scans past theLen-byte buffer into adjacent heap memory. - 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.
- The
SafeUint32Subcheck at line 1932 fires after the over-read and fails, soIScsiBuildKeyValueListreturnsNULLand 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.