SevyDevy

Guide · Backend

OpenSSH's October 2026 Side-Channel Fix Explained: What Admins Should Do

13 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • The SSH compression side channel is a leak between channels, the separate streams that share one SSH connection. All channels pass through one compressor, so encrypted packet sizes can reveal another channel's secret.
  • The paper Crossing the Streams (arXiv 2609.07709, first posted 7 September 2026) showed it. It needs compression on, a secret sent many times, a channel the attacker can write into, and someone measuring packet sizes.
  • OpenSSH 10.6, released on 6 October 2026, switches off LZ77, the step in zlib, SSH's compression library, that swaps repeated text for short pointers. Compression still works but shrinks data less. The bug's public ID, CVE-2026-106582, is rated low (3.7 out of 10).
  • Upgrade both ends to 10.6 or a fixed distribution package; as of 19 October 2026 not every distribution has one. Drop -C, the compress flag, where forwarded ports or shared connections mix traffic. Fix Ansible, a server automation tool that adds -C by default.
  • Beginners can stop after the admin checklist. Engineers then get the small code change behind the fix and why each end protects only what it sends. They also get a tested look at what still varies, and how to confirm a backported fix that keeps an old version number.

What goes wrong when SSH compresses many channels together?

A password on one channel can leak through the packet sizes of another channel, even though every byte stays encrypted. This is the SSH compression side channel, and OpenSSH 10.6 changed its compression to stop it.

Picture a common setup. A developer reaches a private Redis database through an SSH jump box, a server that is the only door into a private network. Redis is a fast in-memory database, and this one needs a password. The setup is made up, but the problem is real.

The developer's SSH config forwards port 6379 on the laptop to Redis. It also turns compression on, because a 4G hotspot was slow. A Redis tool sends the password through that tunnel on every connect.

This guide follows that tunnel. It shows whether someone on the same café Wi-Fi could learn the password, and what to change.

Which words do you need before learning about SSH compression attacks?

Six terms come up again and again. Here they are in plain words.

  • Channel: one stream inside an SSH connection. Your shell is one channel, and each connection through a forwarded port is another. Carrying many channels on one connection is called multiplexing.
  • Port forwarding: SSH listens on a port on your laptop, such as 6379, and carries whatever arrives there to a server on the far side.
  • Compression: making data smaller before sending it. SSH uses zlib, a common compression library. Its LZ77 step replaces repeated text with a short pointer back to the earlier copy.
  • Huffman coding: zlib's other step. It gives frequent bytes shorter codes and rare bytes longer ones. It never looks back for repeats.
  • Ciphertext: the scrambled bytes after encryption. Outsiders cannot read them, but they can count them.
  • Side channel: a leak through something around the data, such as its size or timing, rather than the data itself.

What is the SSH compression side channel?

The SSH compression side channel lets an attacker learn a secret on one channel by sending guesses on another channel and watching encrypted packet sizes. It works because every channel shares one compressor.

RFC 4253, the SSH transport standard, carries the compression context from one packet to the next. A compression context is the compressor's memory of recent text. The standard sets it up afresh after each key exchange, when both ends agree on new encryption keys. The paper points out that one context serves all channels in each direction.

In the developer's setup, anything that connects to port 6379 on the laptop becomes a new channel. It rides the same connection as the shell and the Redis tool.

One connection, one shared compressor

Notice that the attacker's guesses and the Redis password pass through the same compressor before encryption. Encryption hides the bytes, but not how many there are.

Diagram connections:

  • Developer's shell to SSH client (Shell channel)
  • Redis tool on port 6379 to SSH client (Secret on forward channel)
  • Attacker's web page to SSH client (Guesses on a new channel)
  • SSH client to Shared compressor (All channels)
  • Shared compressor to Encryption (Compressed packets)
  • Encryption to Café Wi-Fi (Ciphertext)
  • Café Wi-Fi to Jump box (Same sizes arrive)
  • Developer's shell: Commands typed on the jump box
  • Redis tool on port 6379: Sends AUTH and the password on connect
  • Attacker's web page: Its JavaScript sends guesses to port 6379
  • SSH client: Puts every channel on one connection
  • Shared compressor: One LZ77 memory for all channels
  • Encryption: Hides the bytes, not the packet sizes
  • Café Wi-Fi: An eavesdropper records packet sizes
  • Jump box: Decrypts, decompresses, forwards to Redis

To the authors' knowledge, this is the first compression side-channel attack on SSH. Guesses get in two ways. A network attacker can connect to a forward that listens on all interfaces, meaning other machines can reach it. A malicious web page can, in some browsers, reach a forward that listens only on localhost, your own laptop.

OpenSSH binds local forwards to localhost by default. GatewayPorts is the ssh_config setting that decides who may reach your forwards. Setting it to yes, or giving an explicit bind address such as * or a network address, opens them to other machines.

How does compression leak a secret through packet size?

A guess that matches the secret compresses better, so its packet comes out smaller. LZ77 replaces the matching part with a short pointer to the earlier copy of the secret.

Think of one bill pad shared by two customers at a shop. When an item repeats, the shopkeeper writes "same as line 3" instead of the full name. A stranger who sees only how long each bill is can still tell that something repeated.

  • The shared bill pad is the shared compression context.
  • Writing "same as line 3" is an LZ77 back-reference, the short pointer.
  • The stranger who sees bill lengths is the eavesdropper counting ciphertext bytes.
  • The second customer is the attacker, who chooses what to order.

Where the picture breaks: a real back-reference saves a few bits, not a whole line. SSH also rounds packets up to whole blocks. So one guess rarely changes the size alone, and the paper pads its guesses to line them up with block edges.

A Python toy that shows the size leak

This toy uses the zlib settings OpenSSH uses before and after 10.6. Each flush ends one packet but keeps the shared memory, as SSH does. Packet 1 is the secret; packet 2 is a guess.

python
import zlib

SECRET = b"AUTH rasmalai\r\n"  # the Redis login on the forward channel


def packet_size(guess, after_fix):
    if after_fix:  # OpenSSH 10.6: Huffman coding only, no LZ77
        shared = zlib.compressobj(
            zlib.Z_BEST_SPEED, zlib.DEFLATED, 15, 8, zlib.Z_HUFFMAN_ONLY
        )
    else:  # before 10.6: normal zlib at level 6
        shared = zlib.compressobj(6)
    shared.compress(SECRET)  # packet 1: the secret
    shared.flush(zlib.Z_PARTIAL_FLUSH)  # end the packet, keep the memory
    out = shared.compress(b"AUTH " + guess)  # packet 2: a guess
    out += shared.flush(zlib.Z_PARTIAL_FLUSH)
    return len(out)  # the eavesdropper sees only this size


for guess in [b"rasm", b"rasa", b"kesa"]:
    print(guess.decode(), packet_size(guess, False), packet_size(guess, True))

On Python 3.13 with zlib 1.3, it prints rasm 5 12, then rasa 6 12, then kesa 9 12. The two numbers are packet 2's size in bytes, before the fix and after it.

CRIME and BREACH did this to HTTPS first

The same idea broke HTTPS twice. CRIME (2012) used compression inside TLS, the encryption layer behind HTTPS. BREACH (2013) used compressed HTTP responses. The paper notes that TLS removed compression entirely after CRIME, and SSH did not.

How does the Crossing the Streams attack guess a password?

It guesses one character at a time. For each position it tries every letter, keeps the one that makes traffic smallest, then moves on to the next position.

The guessing loop, one character at a time

Notice that every round needs the secret sent again. The attacker never decrypts anything; it only compares byte counts.

Diagram connections:

  • Flush the memory to Secret is sent again (Clean memory)
  • Secret is sent again to Inject one guess (Secret now in memory)
  • Inject one guess to Count the bytes (Guess compressed)
  • Count the bytes to Clear winner yet? (Sizes per letter)
  • Clear winner yet? to Flush the memory (No, run another round)
  • Clear winner yet? to Keep the letter (Yes)
  • Flush the memory: Send 32 KiB of random data
  • Secret is sent again: Victim's tool sends the password
  • Inject one guess: Known part + letters found + new letter
  • Count the bytes: Eavesdropper totals the traffic
  • Clear winner yet?: Is one letter ahead by the commit margin?
  • Keep the letter: Add it and start the next position

In a long-lived tunnel, each round starts by flushing the compressor's memory with 32 KiB (32,768 bytes) of random data. The commit margin is the lead a letter needs before the attacker accepts it. A bigger margin means fewer mistakes but more rounds.

So the secret must travel many times. The paper notes that a network attacker could force an unattended client to reconnect and log in again. It would do that with TCP reset packets, which tell a computer to drop a connection.

What the paper measured

The authors recovered an 8-letter lowercase secret. Their least noisy test needed at most 276 guesses. It targeted the sudo password, the password for admin commands, that an Ansible run sends. Ansible is a tool that runs commands on many servers over SSH.

Through a web page, a much noisier path, the median run took about 27,600 to 47,200 guesses across three attack settings. Those runs used Firefox, which lacks Private Network Access, a browser check that limits public pages reaching local services.

Chromium, the open-source browser project behind Google Chrome, has that check. From version 142 it also asks the user once before a page may reach local services.

The paper tested Chromium 149 in an easier case, where the attacker already knew the start of the secret. It reports a 67 to 69 percent success rate over 100 trials, but does not say whether the permission was granted.

The paper's verdict: Private Network Access reduces the attack surface but does not remove it.

The measurements use ChaCha20-Poly1305, the encryption method recent OpenSSH picks by default. For AES-based modes, another common family that pads to 16-byte blocks, the paper argues the attack still applies but needs more guesses.

Who is at risk from the SSH compression side channel?

The paper's attack needs all of these on one connection. It shows the attack works in concrete setups; it does not measure how often those setups occur.

  • Compression is switched on for the connection.
  • At least two channels share the connection in the same direction: one carries a secret, another can carry the attacker's guesses.
  • The secret is sent again and again, for example a password on every reconnect.
  • The attacker can see the size of the encrypted traffic, for example on the same Wi-Fi.
  • The attacker can push partly chosen text into one channel, through a forwarded port or a web page.

The developer's tunnel meets every condition. Compression is on, the Redis password goes out on every connect, and a malicious page in Firefox could send guesses to port 6379.

Defaults that decide your exposure (OpenBSD manual pages dated 9 October 2026; ansible-core 2.21.5)
Setting or toolDefaultWhy it matters here
Compression in ssh_config (client)noCompresses only with -C or Compression yes.
Compression in sshd_config (server)yes, after loginThe server accepts compression whenever a client asks.
[email protected] (delayed compression)The only compression method sshd offers by default (9.6p1 source)It starts after login, so shells, forwards and passwords are still compressed.
ControlMaster in ssh_confignoWhen on, many sessions share one connection and one compressor.
GatewayPorts in ssh_confignoLocal forwards listen on localhost only, out of a network attacker's reach.
Ansible ssh_args-C -o ControlMaster=auto -o ControlPersist=60sCompression and connection sharing are both on.

The paper surveyed 33 SSH clients. It found 29 can be set up in a vulnerable way, and 4 prefer compression by default. None both prefers compression and opens forwards on all interfaces by default.

Citing July 2026 scans by Censys, an internet-scanning service, the paper says 28.1 million of 30.2 million SSH servers (93 percent) offer one of the two zlib methods. Offering compression is not the same as using it.

What did OpenSSH 10.6 change, and is there a CVE?

OpenSSH 10.6 disables the LZ77 dictionary coder in ssh, the client, and in sshd, the server. The release notes cite the Crossing the Streams paper. They say LZ77's search buffer, its memory of recent text, is shared across all channels.

The Compression option stays, with the same defaults in the manuals: no for the client, yes for the server. The release notes warn that compression will be less effective.

The release notes give no CVE for this change. A CVE is the public ID for a known security bug. A separate record, CVE-2026-106582, was published on 6 October 2026 for OpenSSH before 10.6. Its CVSS 3.1 score, a 0 to 10 severity scale, is 3.7, rated low.

Many servers run a distribution's package, not upstream OpenSSH. Ubuntu's security notice USN-8928-1, dated 14 October 2026, fixes 22.04, 24.04 and 26.04. The fixed openssh-client versions are below.

Where the fix stands by distribution, as of 19 October 2026 (Ubuntu USN-8928-1 and the Debian security tracker)
ReleaseFixed package versionStatus
Ubuntu 22.04 LTS1:8.9p1-3ubuntu0.18Fixed on 14 October 2026
Ubuntu 24.04 LTS1:9.6p1-3ubuntu13.20Fixed on 14 October 2026
Ubuntu 26.04 LTS1:10.2p1-2ubuntu3.8Fixed on 14 October 2026
Debian 12 (bookworm)None yetVulnerable, marked a minor issue and postponed
Debian 13 (trixie)None yetVulnerable, marked a minor issue; no security advisory planned (no-dsa)
Debian testing and unstable1:10.6p1-1Fixed by the 10.6 upgrade

What should admins do about SSH compression in 2026?

Upgrade, then turn compression off wherever trusted and untrusted traffic share a connection. OpenSSH's release notes say its documentation already advised against that mix, and the current manuals say so under Compression. The paper does not test Huffman-only coding; its top countermeasure is to disable SSH compression.

  1. Upgrade both ends

    Install OpenSSH 10.6 or your distribution's fixed package on laptops and servers. On Ubuntu 24.04, a fixed client prints OpenSSH_9.6p1 Ubuntu-3ubuntu13.20 in ssh -V; earlier package versions lack the fix.

  2. Find where compression is on

    Run ssh -G HOST | grep -i '^compression' for each host you use. It prints compression no or compression yes after reading every config file and flag. Also search scripts for ssh -C.

  3. Check the server side

    On a jump box you run, sshd -T | grep -i '^compression' shows the live setting. Compression no in sshd_config refuses compression for every client, whatever it asks for.

  4. Fix tools that add -C

    In ansible.cfg, under [ssh_connection], set ssh_args = -o Compression=no -o ControlMaster=auto -o ControlPersist=60s. Your value replaces the whole default. The -o Compression=no also overrides a Compression yes in your own SSH config, which Ansible reads. Then ansible-config dump -t connection | grep ssh_args should show it without -C.

  5. Keep forwards local

    Leave GatewayPorts at no and never bind a forward to *. Close a database forward before you browse, because some browsers let a web page reach localhost.

  6. Use a fresh connection for secrets

    The paper suggests a separate SSH connection for sensitive work, not one shared through ControlMaster.

  7. Rotate passwords that crossed a risky connection

    If a repeated password crossed a compressed connection that also carried forwards, changing it is cheap insurance. That is our judgement; the paper shows feasibility, not attacks in the wild.

Should this connection compress?

Notice that only one path ends in turning compression off: compression on plus mixed channels. Every path still upgrades.

Diagram connections:

  • A connection you own to Is compression on? (Check ssh -G)
  • Is compression on? to Mixed channels? (Yes)
  • Is compression on? to Low risk (No)
  • Mixed channels? to Turn compression off (Yes)
  • Mixed channels? to Low risk (No, one trusted stream)
  • A connection you own: Shell, forward or Ansible run
  • Is compression on?: ssh -C, Compression yes or a tool default
  • Mixed channels?: Forwards, ControlMaster or outside input
  • Turn compression off: Upgrade to 10.6 and drop -C here
  • Low risk: Still upgrade to 10.6

The developer removes Compression yes from the jump box entry and gzips large log files before copying them. ssh -G now prints compression no for that host.

Beginners can stop here. The rest is for engineers who want the code change, the edge cases and the rest of the release.

What exactly did the OpenSSH 10.6 fix change in the code?

The core change is in one function in packet.c, OpenSSH's packet layer. Before the fix, start_compression_out set up zlib with deflateInit at level 6, zlib's default balance of speed and size.

After it, the function calls deflateInit2 with the Z_HUFFMAN_ONLY strategy, so zlib uses Huffman coding and never back-references. The other changed lines only drop the now-unused level argument.

We read this in Ubuntu's copy of the upstream commit, dated 22 September 2026, shipped as debian/patches/CVE-2026-106582.patch in package 1:9.6p1-3ubuntu13.20. Copying a fix into an older version like this is called a backport.

Each end protects only what it sends

The patch changes only the sending side. The receiving side still calls inflateInit, so a fixed end still decodes LZ77 data from an old peer. Secrets can travel both ways: a typed password goes up, and a token read from Redis comes back down.

That is why both ends need the upgrade. A fixed laptop talking to an old jump box still gets LZ77-compressed replies.

What Huffman-only still varies, in our test

We sent one 46-byte request after 303 different earlier packets on a shared zlib 1.3 stream, with OpenSSH's before and after settings. Before the fix, it took 6 bytes when the same text came earlier, against 48 or 49 bytes after unrelated packets.

After the fix, with SSH's partial flush, it took 48 bytes 280 times and 49 bytes 23 times. The same-text case was 49 bytes, not smaller. With a byte-aligned flush as a control, it was always 52 bytes.

So the one-byte wobble comes from leftover bits at the end of earlier packets, not from repeated text. The release notes do not mention it, as of 19 October 2026. The script and its output are in our test files.

How to confirm a backported fix that keeps an old version number

Distributions often keep the old version number when they backport. On Debian or Ubuntu, dpkg-query -W openssh-client prints the package version, for example 1:9.6p1-3ubuntu13.20.

On Ubuntu, the changelog names the patch too. zcat /usr/share/doc/openssh-client/changelog.Debian.gz | grep -m1 CVE-2026-106582 prints a line naming debian/patches/CVE-2026-106582.patch when the fix is in.

No output on Ubuntu means the fix is missing. On a package built from 10.6 or later, such as Debian unstable's 1:10.6p1-1, the version number itself tells you.

What else changed in OpenSSH 10.6?

OpenSSH 10.6 also enables a hybrid post-quantum signature, ssh-mldsa44-ed25519. It pairs a classic algorithm with one built to resist future quantum computers. Keys made with the earlier experimental "@openssh.com" version must be regenerated or removed.

The server gains WarnWeakCrypto, on by default, which logs weak or discouraged algorithms, such as key agreement that is not post-quantum safe. And ssh now rejects $ and \ in usernames typed on the command line; usernames set in config files are exempt.

How do you explain the SSH compression side channel in a system design interview?

Explain it as a shared resource that leaks through size. Then give the fix and its cost.

  1. State the leak

    Channels share one compressor, so guesses on one channel change packet sizes that depend on another channel's secret.

  2. Name the conditions

    Compression on, two channels sharing it, a repeated secret, an injection channel and an eavesdropper. Without any one, the paper's attack fails.

  3. Link it to CRIME and BREACH

    Same pattern as the HTTPS attacks, after which TLS dropped compression.

  4. Give the fix

    OpenSSH 10.6 keeps Huffman coding and drops LZ77 on the sending side. Admins drop -C where traffic is mixed and compress inside the application.

  5. Discuss the trade-off

    Less compression means more bytes. Per-channel contexts would cost memory and break the standard; separate connections cost setup time.

Questions people ask

Is SSH compression on by default?
Not in the OpenSSH client, where Compression defaults to no. It turns on with -C or Compression yes, and an OpenSSH server then agrees after login, because its own default is yes. Ansible passes -C by default. The paper found 4 of 33 surveyed clients prefer compression by default.
Am I affected if I never use port forwarding?
Your risk is much lower. The attack needs a channel that carries the attacker's guesses, and forwarded ports are the main way in. Shared ControlMaster connections put more sessions on one compressor. The paper also sketches a variant over X11 forwarding, which shows a remote app's windows on your screen, but only partly tested it.
Does delayed compression, [email protected], protect me?
No. Delayed compression only waits until after login, which keeps the compressor away from login messages. Your shell, forwards and passwords all travel after login, so the paper says it does not stop this attack.
Is there a CVE for the SSH compression side channel?
Yes, though OpenSSH's release notes do not list one. CVE-2026-106582 was published on 6 October 2026 for OpenSSH before 10.6. Its CVSS 3.1 score is 3.7, rated low, with a low impact on confidentiality only.
My distribution has no fix yet. What should I do?
Turn compression off, which protects you on any version. Set Compression no in ssh_config for clients and in sshd_config for servers you run. As of 19 October 2026, Debian 12 and 13 list the bug as a minor issue with no fixed package, so this is the protection there.
Can I still use ssh -C on a slow connection?
Yes, when the connection carries one trusted stream, such as a single shell. Avoid it alongside forwards, shared ControlMaster sessions or anything outsiders can send data into. On 10.6, -C shrinks data less, because only Huffman coding is left.
Do other SSH clients like PuTTY or Dropbear need changes?
The OpenSSH fix covers only OpenSSH. The paper's survey of 33 clients includes PuTTY and Dropbear, and found 29 that can be set up in a vulnerable way. Four prefer compression by default: Ansible, Dropbear, MobaXTerm and Termux. Check your own client's compression and forwarding settings and its vendor's advisories.

Sources and further reading

Practise this with Lala

Practise