[Bug 294623] Remote Denial of Service via TCP Syncache Exhaustion Global Syncache Attack with Severe Impact on SSH

From: <bugzilla-noreply_at_freebsd.org>
Date: Sat, 18 Apr 2026 20:15:26 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=294623

            Bug ID: 294623
           Summary: Remote Denial of Service via TCP Syncache Exhaustion
                    Global Syncache Attack with Severe Impact on SSH
           Product: Base System
           Version: 15.0-RELEASE
          Hardware: amd64
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: igor@bsdtrust.com

The vulnerability resides in the TCP Syncache mechanism of FreeBSD 15, a global
structure (not per-port) with a fixed limit of 30 entries per bucket. A remote
attacker can completely exhaust this table by sending SYNs with a spoofed
source (--rand-source) against any open TCP port.

The command "hping3 -S -p 22 --flood --rand-source -d 32 192.168.202.110"
launches an extremely high-rate SYN flood with completely spoofed random source
IPs on every packet, generating thousands of unique 5-tuples that rapidly fill
the global TCP Syncache buckets (limited to 30 entries each), immediately
triggering syncache_pause(), setting V_tcp_syncache.paused = true system-wide,
forcing syncookie mode on the entire system, and drastically reducing the
delivery rate of legitimate SYNs to sshd.

SSH is affected by this vulnerability because the TCP Syncache is a global
kernel structure; as long as any TCP port is open on the system, an attacker
can trigger the system-wide protection mode (paused = true) by flooding that
port with spoofed SYNs, severely degrading or denying service to SSH even if
the attack is not directed at port 22.

When the cache fills up, the kernel activates the global protection mode
(paused = true), forcing the use of syncookies across the entire system. This
drastically reduces the rate of legitimate SYNs delivered to TCP services.
Observed impact on SSH (provided debug):

 - Initial handshake completes (Connection established at 15:54:11).
 - KEX starts at 15:54:30 but suffers an extreme delay.
 - Connection is closed by the server at 15:56:31 (approximately 2 minutes
after the KEXINIT).
 - When login is possible, the session becomes very unusable (lag, duplicated
characters, just not run commands, keyboard input, activation of
obfuscate_keystroke_timing).


2. Technical Analysis – Kernel Code (tcp_syncache.c)

2.1 syncache_add() – Entry Point for Every SYN

struct socket *
syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
    struct inpcb *inp, struct socket *so, struct mbuf *m, ...)
{
    ...
    sc = syncache_lookup(inc, &sch);   // lookup in the global hash
    if (sc == NULL) {
        sc = uma_zalloc(V_tcp_syncache.zone, M_NOWAIT | M_ZERO);
        ...
        syncache_insert(sc, sch);      // ← CALLS THE CRITICAL POINT
    }
}

Explanation: Every SYN (legitimate or spoofed) passes through here. If no entry
exists, a new one is created and syncache_insert() is called.

2.2 syncache_insert() – The Heart of the Vulnerability

static void
syncache_insert(struct syncache *sc, struct syncache_head *sch)
{
    SCH_LOCK(sch);

    if (sch->sch_length >= V_tcp_syncache.bucket_limit) {   // default = 30
        syncache_pause(&sc->sc_inc);          // ← ACTIVATES GLOBAL PROTECTION
        sc2 = TAILQ_LAST(&sch->sch_bucket, sch_head);
        sch->sch_last_overflow = time_uptime;
        syncache_drop(sc2, sch);              // discards the oldest entry
    }

    TAILQ_INSERT_HEAD(&sch->sch_bucket, sc, sc_hash);
    sch->sch_length++;
    ...
}

Explanation: This is the failure point. With --rand-source, each SYN creates a
unique tuple → rapidly fills the buckets. When it reaches 30, it globally
activates syncache_pause().

2.3 syncache_pause() – Activation of Global Protection

static void
syncache_pause(struct in_conninfo *inc)
{
    ...
    V_tcp_syncache.paused = true;          // ← AFFECTS THE ENTIRE SYSTEM
    ...
}

Explanation: From this point on, the kernel considers itself under SYN flood
and changes the behavior of all TCP ports.

2.4 syncache_cookiesonly() – MAIN BOTTLENECK

static inline bool
syncache_cookiesonly(void)
{
    return ((V_tcp_syncookies && V_tcp_syncache.paused) ||   // ← HERE
            V_tcp_syncookiesonly);
}

Explanation: When paused = true, the kernel stops using the normal (fast)
syncache and switches to syncookies. This drastically reduces the rate of
legitimate SYNs delivered to sshd.

2.5 syncache_respond() – SYN|ACK Response Using Syncookie

static int
syncache_respond(struct syncache *sc, int flags)
{
    ...
    // Builds SYN|ACK using syncookie when paused = true
    ...
}

3. Technical Analysis – SSH Code (sshd.c)

3.1 server_accept_loop() – accept() Loop

static void
server_accept_loop(...) {
    ...
    *newsock = accept(listen_socks[i],   // ← sshd calls accept()
        (struct sockaddr *)&from, &fromlen);
    ...
}

Correlated debug:
text[15:54:11.722] debug1: Connection established.

Explanation: accept() returns success (valid FD), but the kernel is already
delivering very few packets to this connection.

3.2 Key Exchange (KEX)
text[15:54:30.818] debug3: send packet: type 20
[15:54:30.819] debug1: SSH2_MSG_KEXINIT sent
...
[15:56:31.952] Connection closed by 192.168.202.110 port 22

Explanation: KEX starts at 15:54:30 but suffers an extreme delay (~2 minutes)
because the kernel delivers very few packets. The server eventually closes the
connection due to timeout.

3.3 keyboard-interactive (password prompt)

textdebug2: input_userauth_info_req: num_prompts 1
(igor@192.168.202.110) Password for igor@nepph16:

Explanation: The prompt takes a very long time to appear because the kernel
delivers very few packets.

3.4 obfuscate_keystroke_timing (unusable terminal)

textdebug3: obfuscate_keystroke_timing: starting: interval ~20ms
debug3: obfuscate_keystroke_timing: stopping: chaff time expired (...)

Explanation: OpenSSH detects high latency/packet loss in the session and
activates the anti-timing attack protection. This causes extreme lag,
duplicated characters, and makes the terminal unusable.


================================================================================
                  TCP Syncache Global - 512 Buckets (Hash Table)
================================================================================

Attacker
   │
   ├─► SYN spoofed (--rand-source)   [random source IP on every packet]
   │      5-tuple unique → different hash
   │
   ▼
syncache_insert()   ← called for EVERY SYN
   │
   ├─► if (sch->sch_length >= 30) {          ← BUCKET FULL!
   │       syncache_pause(&sc->sc_inc);
   │       V_tcp_syncache.paused = true;     ← GLOBAL PROTECTION ACTIVATED
   │    }
   │
   ▼
Bucket #N (example of a full bucket)
   +-------------------------------+
   | sch_mtx (lock)                |
   | sch_length = 30/30  ← FULL    |   ← 30 struct syncache entries
   |                               |
   | [SYN spoofed 1]               |
   | [SYN spoofed 2]               |
   | ...                           |
   | [SYN spoofed 30]              |
   +-------------------------------+
          │
          ▼
syncache_pause() → paused = true  (GLOBAL - affects the entire kernel)

          │
          ▼
syncache_cookiesonly() returns true
          │
          ▼
FORCES SYNCookies ON THE ENTIRE SYSTEM
   (SipHash + rate-limit)
          │
          ▼
Legitimate SYN rate drops drastically
   (from hundreds per second → only a few per second)

================================================================================
                  IMPACT ON SSH (port 22) - Flow with Debugs
================================================================================

sshd.c
   │
   ├─► server_accept_loop()
   │      accept()  ← returns valid FD
   │
   │   [15:54:11.722] debug1: Connection established.
   │
   ▼
KEX (Key Exchange)
   │
   ├─► SSH2_MSG_KEXINIT sent
   │   [15:54:30.819] debug1: SSH2_MSG_KEXINIT sent
   │
   │   ← Extreme delay (~2 minutes) due to lack of packets
   │
   ▼
   [15:56:31.952] Connection closed by 192.168.202.110 port 22

When login succeeds:
   │
   ├─► keyboard-interactive (password prompt)
   │
   ├─► obfuscate_keystroke_timing activated
   │      debug3: obfuscate_keystroke_timing: starting: interval ~20ms
   │      (sends chaff packets to mask timing)
   │
   ▼
   Terminal unusable (lag, duplicated characters, freezing)

================================================================================
Attacker (high-rate spoofed SYN flood)
    → fills Buckets (30/30)
    → syncache_pause()
    → paused = true (GLOBAL)
    → syncookies
    → sshd receives almost no packets
    → KEX takes 2 min + terminal unusable


Credits:
Author: Igor Gabriel Sousa e Souza
Email: igor@bsdtrust.com
LinkedIn: https://www.linkedin.com/in/igo0r


Thanks!

-- 
You are receiving this mail because:
You are the assignee for the bug.