You're on-call. An alert fires — request latency has climbed from 40ms to 40 seconds. You pull up the dashboard. CPU is at 1%. Memory is stable. No network saturation. The health check endpoint still returns 200 OK.
The system appears alive. It is not.
This is not a performance issue. It is an execution failure — and traditional monitoring is blind to it.
This is Phase 03 of the Offensive Socket Security series: ThreadPool Starvation
The Core Insight
A socket connection is not just a transport channel—it is a potential execution anchor.
If your system allows a network operation to block a thread indefinitely, you are allowing an external entity to control your execution lifecycle. Under this condition, compute capacity is no longer governed by the system, but by whoever is connected to it.
ThreadPool starvation is not caused by high load. It is caused by unreleased execution slots.
The Vulnerable Architecture
In a typical synchronous implementation, each connection is handled by a ThreadPool worker thread.
CONNECT → Client establishes TCP connection
DISPATCH → Assigned to ThreadPool thread
RECEIVE → Server calls blocking Read()
WAIT → Thread blocks until data arrivesIf no timeout is enforced, the thread remains blocked indefinitely. Under normal conditions this is invisible. Under adversarial conditions, it is the mechanism of complete execution collapse.
The Exploit Flow: Execution Pinning
Because the system assumes every connection will eventually send data, an attacker can exploit this by doing nothing.
[ATTACKER] CONNECT (multiple times)
[SERVER] Assigns threads
↓
[ATTACKER] Sends nothing
↓
[SERVER] Threads blocked in Read()
↓
[SERVER] Available threads → 0
↓
[LEGIT USERS] Requests queued
↓
[SERVER] No execution capacityThe system does not crash. It does not spike CPU. It simply stops processing work.
The attacker does not consume resources—they reserve execution capacity.
Why This Breaks in .NET
The behavior is amplified by how the .NET ThreadPool works.
The ThreadPool uses a hill-climbing algorithm to inject threads gradually based on throughput. When threads are blocked, throughput drops, but new threads are introduced slowly. Under attack, newly created threads are immediately consumed by additional blocked connections, preventing recovery.
This is why the failure is invisible to standard monitoring. The ThreadPool continues injecting threads, CPU stays low, and the system reports healthy — while execution capacity drains to zero.
Real-World Impact
This is not a socket-specific failure. The same boundary violation appears across modern systems:
Join the conversation! Your thoughts help the community grow.