# CVE-2023-28231 — Windows DHCPv6 Server `dhcpssvc.dll` Heap Overflow via Unbounded Nested Relay-Forward Hop Count in `ProcessRelayForwardMessage`

---

## Summary

| | |
|---|---|
| **Product** | Microsoft Windows — `dhcpssvc.dll` (DHCP Server Service, DHCPv6) |
| **CVE ID** | CVE-2023-28231 |
| **Impact** | Remote Code Execution |
| **MSRC severity** | Critical |
| **CWE** | CWE-122: Heap-based Buffer Overflow |
| **Patch Date** | April 11, 2023 |
| **Pre-patch binary** | `dhcpssvc.dll` 10.0.17763.3469 (Server 2019, Mar 2023) — SHA256 `a847fe2290b474a0aa14d1d83083ae24c9ed36b174ffc59b5e989a115ee658c6` |
| **Post-patch binary** | `dhcpssvc.dll` 10.0.17763.4252 (KB5025229, Apr 2023) — SHA256 `f2cdcfc6f9c374b47ca032a9766a8e28319c0fd82b42484c326667bff85ad895` |
| **Fix gating** | **None** — unconditional bounds check (no CFR/KIR flag) |

---

## Product Description

The DHCP Server Service (`dhcpssvc.dll`, hosted in `svchost.exe` as
`NT AUTHORITY\NETWORK SERVICE`) assigns IPv6 addresses and configuration via
DHCPv6 over UDP/547. DHCPv6 relay agents forward client messages to servers
using **Relay-forward** messages (msg-type 12), each carrying a **Relay Message**
option (option-code 9) whose data is the message being relayed. Relay-forward
messages can be **nested** — a relay agent may relay to another relay agent — so
a single packet can contain a chain of nested Relay-forward messages. Because the
DHCP server parses these packets straight off the network, every field is
attacker input, and the service is reachable network-adjacently without
authentication.

---

## Vulnerability Summary

`ProcessRelayForwardMessage` allocates a **fixed 1664-byte** on-object buffer — an
array of **32 entries of 0x34 (52) bytes** — to record each nested Relay-forward
hop, plus a hop **counter**. As it walks the nesting, it writes one entry per
nested Relay-forward and increments the counter, **without ever checking the
counter against the 32-entry maximum**:

```c
// ProcessRelayForwardMessage (dhcpssvc.dll 10.0.17763.3469) — PRE-PATCH, from our diff
memset((char *)param_1 + 0x644, 0, 0x680);          // 0x680 = 1664 = 32 * 0x34
...
// for each nested Relay-forward encountered:
lVar18 = (ulonglong)*(uint *)((char *)param_1 + 0xcc4) * 0x34;   // counter * 52
*(undefined2 *)(lVar18 + 0x664 + (longlong)param_1) = *(undefined2 *)(pbVar23 + 0x20);
*(byte **)(counter * 0x34 + 0x66a + (longlong)param_1) = ...;    // write into slot
// counter (+0xcc4) incremented — NO bound check against 32
```

The counter lives at object offset `+0xcc4`; the entry array base is `+0x644`
(0x664/0x666/0x66a are field offsets within each 0x34-byte slot). With more than
32 nested Relay-forward messages, `counter * 0x34` indexes **past the 1664-byte
array**, and the per-slot writes corrupt adjacent heap with attacker-controlled
bytes — a controlled heap overflow in the DHCP service.

---

## Prerequisites and Constraints

- **Network-adjacent, unauthenticated.** Reachable by anything that can deliver
  DHCPv6 to UDP/547 (DHCP is link-scoped / non-routable, hence *adjacent* rather
  than fully remote, but no credentials are needed).
- No user interaction, no special hardware.
- Corruption is in the `NETWORK SERVICE` DHCP service; ZDI notes a successful
  compromise can then be escalated toward SYSTEM.
- The overflow contents come from the nested message fields, so this is a
  *controlled* overflow suitable for grooming.

---

## Vulnerability Details

### Call Chain

```
Network-adjacent attacker (no auth), UDP/547:
  DHCPv6 Relay-forward (msg-type 12) with >32 nested Relay-forward messages
        ↓
DHCP Server service (dhcpssvc.dll, NETWORK SERVICE):
  ProcessClientMessage
    -> ProcessRelayForwardMessage          [*** heap overflow ***]
         memset(obj+0x644, 0, 0x680)        // 32 x 0x34 entry array
         per nested hop: write entry[counter], counter++   // counter never bounded
```

### Root Cause

The nesting depth is attacker-controlled but the destination array is a fixed 32
slots. The code increments and indexes with the hop counter but never asserts
`counter < 32`, so a chain longer than 32 Relay-forward messages writes beyond
the allocation. Classic missing-bound / off-the-end array write (CWE-122).

### The patch

The April 2023 build adds the missing check — reject once the counter reaches 32
(`0x1f < counter`, i.e. `counter >= 32`) and return an error before any
out-of-range write:

```c
// ProcessRelayForwardMessage (10.0.17763.4252) — PATCHED, from our diff
uVar6 = *(uint *)((char *)param_1 + 0xcc4);   // hop counter, read once
if (bVar5 - uVar6 != (uint)pbVar23[1]) { ... goto error; }
if (0x1f < uVar6) {                           // *** counter >= 32 -> reject ***
    ...
    return 0x4e2f;                            // error, no write
}
lVar18 = (ulonglong)uVar6 * 0x34;             // only reached when counter <= 31
```

`0x4e2f` (20015) is the DHCP error returned; the write path is now gated on
`counter <= 31`, so the 33rd nested Relay-forward is refused instead of
overflowing the array.

### Patch Completeness Assessment

**The fix is a plain, unconditional bounds check** — there is no Controlled
Feature Rollout flag or Known Issue Rollback toggle guarding it. This is worth
noting against the more recent CVEs in this corpus (CLFS 2025–2026, `lserver`
CVE-2024-38077, `tapisrv` CVE-2026-25188, `http.sys` CVE-2026-47291), whose
memory-safety fixes ship behind `Feature_NNNN` CFR flags or KIR toggles with the
vulnerable path still compiled in. In April 2023 Microsoft still shipped this
Critical RCE fix as a straight, always-on `return` — the "both paths ship, gated
at runtime" servicing pattern is a later development. Here, patch state **is**
determined by file version.

---

## Detection Guidance

**Network.** On UDP/547, parse DHCPv6 and count Relay-forward messages (msg-type
12) within a single packet — following each Relay Message option (option-code 9)
into the nested message. **More than 32** nested Relay-forward messages in one
packet is the direct exploit indicator (ZDI threshold). Legitimate relay chains
are only a few hops deep.

**Crash signature.** Heap-corruption bugchecks or `svchost.exe` (DHCP Server host)
crashes in `dhcpssvc!ProcessRelayForwardMessage`, in the `NETWORK SERVICE`
process. Special Pool / pageheap on `dhcpssvc.dll` makes the 33rd-hop write land
on a guard page.

**Behavioural.** DHCPv6 traffic from an unexpected source to a DHCP server role,
carrying deeply nested Relay-forward encapsulation — anomalous for normal relay
topologies.

---

## Mitigation

- **Patch** (MS April 2023). The only complete remediation; the fix is a direct
  bounds check, effective as soon as the updated `dhcpssvc.dll` is loaded.
- Restrict UDP/546–547 to trusted relay agents / segments; DHCP is link-scoped
  and should not traverse untrusted boundaries.

---

## References

- ZDI / Trend Micro Research — *CVE-2023-28231: RCE in the Microsoft Windows
  DHCPv6 Service* (Guy Lederfein, Lucas Miller). `thezdi.com/blog/2023/5/1/...`
- MSRC advisory — CVE-2023-28231 (DHCP Server Service Remote Code Execution)
- Full binary diff: `/data/patch_diffs/dhcpssvc_dll-cve-2023-28231-ghidriff.md`
