# CVE-2026-41089 — Windows Netlogon `netlogon.dll` DC-Locator Response Serialization: Stack Overflow + Uninitialized-Stack Disclosure

---

## Summary

| | |
|---|---|
| **Product** | Windows Server — `netlogon.dll` (Netlogon service; DC Locator response serialization) |
| **CVE ID** | CVE-2026-41089 |
| **Impact** | Remote Code Execution |
| **MSRC severity** | **Critical** |
| **CVSS** | 9.8 / 8.5 — `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-121: Stack-based Buffer Overflow (primary); CWE-908/CWE-200: Uninitialized-stack information disclosure (secondary) |
| **Delivery** | **Pre-auth network** — CLDAP (UDP 389), the Netlogon mailslot `\MAILSLOT\NET\NETLOGON`, and RPC/internal paths |
| **KB / Fixed build** | KB5087539 — `netlogon.dll` (Windows Server 2025; fix ships May 2026) |
| **Patch Date** | May 12, 2026 (2026-May) |
| **Pre-patch binary (pre-fix baseline)** | `netlogon.dll` 10.0.26100.7623 — SHA256 `bc121ba6fbdf7a9dcd5a17e5f9101a65f88c267271026a468f116fd189fc327d` |
| **Post-patch binary (fix present)** | `netlogon.dll` 10.0.26100.8521 — SHA256 `412f24dd12b61370a24df5859fe5872b5144425de67eaa9c24a1922e02d5a168` |
| **Feature flag** | `Feature_740537659` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`netlogon.dll` is the Windows Netlogon service (runs as **SYSTEM** on a domain
controller). Its **DC Locator** response path serializes a `SamLogon` / ping
response — Unicode strings (domain name, DNS domain name, computer name), GUIDs,
DNS compression data and Netlogon flags — into a **fixed 528-byte stack buffer**,
advancing a write cursor as it goes. These responses are reachable **before
authentication** over CLDAP (UDP 389), the Netlogon mailslot and RPC.

---

## Vulnerability Summary

This patch fixes **two distinct bugs** in the same serialization path.

### 1. Stack-based buffer overflow (CWE-121) — `BuildSamLogonResponse` / `PrimaryQueryHandler`

The serializers call `NetpLogonPutUnicodeString` to write each Unicode string and
advance the write position, **but ignored its return value**. When an input string
is near the buffer's remaining capacity, the write can run **past the buffer
boundary** or leave the cursor in an **undefined** state; subsequent
`NetpLogonPutBytes` / GUID / flag writes then corrupt adjacent stack memory. The
caller's buffer is only 528 bytes, but a near-max Unicode string combined with a
GUID pair, DNS compression data and version flags can exceed it. Notably the DNS
path (`NlpUtf8ToCutf8`) *already* checked its return and aborted on failure — the
defensive check was missing **only** at the `NetpLogonPutUnicodeString` sites.

```c
// BuildSamLogonResponse (pre-fix) — from our diff
NetpLogonPutUnicodeString(v10, 36, &v27);    // return ignored
NetpLogonPutUnicodeString(a4, 130, &v27);    // return ignored
NetpLogonPutUnicodeString(a1 + 36, 32, &v27);// return ignored -> cursor may be past end
// ... GUID / flag writes then corrupt the stack
```

### 2. Uninitialized-stack information disclosure (CWE-908/CWE-200) — `NlGetLocalPingResponse`

`NlGetLocalPingResponse` declares a 528-byte stack buffer `Src` **without
initializing it**. `PrimaryQueryHandler` / `LogonRequestHandler` write structured
response data into it, then `memcpy` copies `Size[0]` bytes into a heap buffer for
transmission. The **padding / gaps** between aligned and variable-length fields can
still hold **leftover stack data from a previous call** — frame pointer, return
address, authentication-token remnants — which is then sent to the **unauthenticated
remote requester**.

Because Netlogon runs as SYSTEM and both bugs are reachable pre-auth over the
network, severity is Critical (RCE + disclosure).

---

## Prerequisites and Constraints

- Network, unauthenticated (`AV:N`, `PR:N`, `AC:L`): send a crafted DC Locator /
  SamLogon query over CLDAP (UDP 389), the Netlogon mailslot, or RPC.
- Stack overflow: supply a domain / DNS / computer name near the length limit so
  the fixed 528-byte serialization buffer is overrun by the trailing GUID/flag
  writes.
- Disclosure: any ping/logon query returns the response buffer, whose uninitialized
  gaps leak stack memory.

---

## Vulnerability Details

### Root Cause

(1) `NetpLogonPutUnicodeString`'s failure/return was ignored, so serialization
continued from an out-of-bounds or undefined write position into a fixed stack
buffer. (2) The response stack buffer was never zero-initialized, so its
inter-field padding carried prior stack contents into the network response.

### The patch (confirmed — diff, .7623 → .8521; fix ships May 2026)

All three fixes are gated behind `Feature_740537659`:

- **`BuildSamLogonResponse`** — the three `NetpLogonPutUnicodeStringOld` calls are
  replaced with `NetpLogonPutUnicodeString` calls whose **return value is checked**
  in a nested `if` chain; if a string write fails, no further writes occur and an
  error (`1355` = `ERROR_NO_LOGON_SERVERS`) is returned immediately.
- **`PrimaryQueryHandler`** — two `NetpLogonPutUnicodeString` calls gain return
  checks; on failure it `return 0`s instead of serializing from a bad cursor.
- **`NlGetLocalPingResponse`** — `memset(Src, 0, 0x208)` (528 bytes) is inserted
  right after the `NlGlobalMemberWorkstation` check, zeroing the buffer before it
  is populated so no uninitialized stack data is transmitted.

```c
// BuildSamLogonResponse (patched, Feature_740537659) — from our diff
uVar2 = NetpLogonPutUnicodeString((wchar_t*)(param_1+0xd8), 0x24, &local_90);  // 36
if (uVar2) { uVar2 = NetpLogonPutUnicodeString(param_4, 0x82, &local_90);      // 130
  if (uVar2) { uVar2 = NetpLogonPutUnicodeString((wchar_t*)(param_1+0x48), 0x20, &local_90); } }  // 32
if (!uVar2) return 1355;   // string serialization failed -> stop, no further writes

// NlGetLocalPingResponse (patched, Feature_740537659)
if (NlGlobalMemberWorkstation) return 1355;
if (Feature_740537659__private_IsEnabledDeviceUsage())
    memset(local_258, 0, 0x208);   // *** zero the 528-byte response buffer ***
```

The in-house diff confirms `NetpLogonPutUnicodeString` changed from `void` to a
value-returning function (the old body preserved as `NetpLogonPutUnicodeStringOld`),
the return-checked call chains in `BuildSamLogonResponse` / `PrimaryQueryHandler`,
and the `memset(...,0,0x208)` in `NlGetLocalPingResponse` — all gated by
`Feature_740537659`.

### Patch Completeness Assessment

**CFR-gated behind `Feature_740537659`.** The return-checked serialization and the
buffer zeroing run only when the flag is enabled; the original paths still ship
when disabled. Verify `Feature_740537659` is enabled to confirm both fixes are
live. Given the pre-auth/Critical nature, prioritize applying the May 2026 update.

---

## Detection Guidance

**Behavioural.** Pre-auth CLDAP / mailslot / RPC DC-Locator queries with
domain/DNS/computer names near the length limit; Netlogon (`lsass`/`netlogon`)
stack-corruption crashes during SamLogon response building; anomalous bytes
(pointer-like / token-like data) in ping/logon responses on unpatched builds.

**Config.** The fix is CFR-gated — confirm `Feature_740537659` is enabled.

---

## References

- MSRC advisory — CVE-2026-41089 (Windows Netlogon Remote Code Execution), released 2026-05-12, KB5087539.
- Full binary diff: `/data/patch_diffs/netlogon_dll-cve-2026-41089-ghidriff.md`
