← Back to Wiki
Security / SSH

Pinning KexAlgorithms Silently Downgraded My SSH, and the Test That Would Have Caught It

Pinning an explicit list of SSH algorithms feels like the responsible thing to do. It is also how you remove a protection you had for free. A hand-written KexAlgorithms line here quietly dropped post-quantum key exchange on a host that supported it, and nothing failed. The only signal was a warning from the SSH client that nobody reads. The same session produced a second lesson: the obvious way to test a MACs change never consults the setting at all.

Share on X

What actually happened

The goal was ordinary. A scanner had flagged weak MAC algorithms, so the fix was an explicit MACs line, and while in there it seemed sensible to pin KexAlgorithms too. The list came from a hardening guide. It looked strict and modern.

It omitted [email protected]. OpenSSH 9.6 on that host offered it by default, the pinned list replaced the default entirely, and key exchange fell back to classical curve25519. No error. No log line on the server. Every connection continued to work exactly as before.

BE WARNED: an algorithm list replaces the default, it does not add to it. That is the whole bug in one sentence. Every algorithm you fail to list is an algorithm you have turned off, including ones added since the guide you copied was written. Post-quantum key exchange is the one that hurts, because it is recent, it is on by default in current OpenSSH, and losing it produces no visible symptom.

The warning nobody reads

Modern OpenSSH clients do tell you. It looks like this, and it scrolls past above the banner:

** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.

Take it seriously. "Store now, decrypt later" is not a hypothetical for a host with a long life. Traffic captured today is decryptable the day the maths changes, and a key exchange is exactly the part of the session that matters for that.

Prefer adding to the default over replacing it

OpenSSH lets you modify the default list rather than overwrite it, and this is almost always what you actually want:

# remove specific weak algorithms, keep everything else including future additions
KexAlgorithms -diffie-hellman-group1-sha1,diffie-hellman-group14-sha1

# or put one first without discarding the rest
KexAlgorithms ^[email protected]

The - prefix subtracts from the default. The ^ prefix moves entries to the front. Both survive an OpenSSH upgrade adding something new and good, which a hardcoded list does not. Pin the literal list only when a compliance document demands that exact string, and then diff it against ssh -Q kex on every upgrade.

Confirm what was actually negotiated

Never trust the config file. Ask the connection:

ssh -vv <host> 2>&1 | grep -i "kex: algorithm"
# kex: algorithm: [email protected]

# and see what this build even supports
ssh -Q kex

The MACs test that proves nothing

Here is the second trap, and it invalidates most people's verification of a MACs change.

You set a strict MACs line. You test it by connecting with a weak MAC forced, expecting a rejection. It connects. You conclude the setting did not apply.

BE WARNED: an AEAD cipher never consults the MACs list. [email protected] carries its own integrity, so when it is negotiated the MAC directive is simply not used. Your test connected because MACs were irrelevant to that session, not because your config failed. Force a non-AEAD cipher and the directive comes back into play:
# correct test. Expect: no matching MAC found
ssh -o Ciphers=aes256-ctr -o MACs=hmac-sha1 <host>

# control. A permitted MAC on the same cipher should still connect
ssh -o Ciphers=aes256-ctr -o MACs=hmac-sha2-512 <host>
Run both. One test that fails proves a rejection exists. The pair proves you rejected the right thing.

The general shape

Every item here is the same mistake wearing different clothes. A security change that only ever makes things stricter is easy to verify, because failure is loud. A security change that replaces a set of defaults can silently remove something, and the removal is invisible unless you go looking for what is no longer there.

So after any algorithm change, ask what the connection negotiated rather than what the file says. The fleet-wide version of this fix, applied across 22 hosts, is in the SSH MAC hardening guide.

When this isn't your problem