Your Erlang TLS 1.3 client can trust a server with no certificate
How to fix CVE-2026-89422: upgrade Erlang/OTP to 29.1.1, 28.5.0.7, or 27.3.4.18
Contents
An Erlang/OTP TLS 1.3 client can finish ssl:connect against a peer that never sent a certificate. Erlang published CVE-2026-89422 today (CVSS 4.0 9.3 Critical, CWE-322). If the ServerHello carries a pre_shared_key extension the client never offered, the default client marks the handshake as resumed, skips certificate path validation, hostname checks, verify_fun, CRL, and OCSP, and still returns {ok, Socket}. The peer needs no cert, no private key, and no prior session. After that handshake the attacker holds the traffic keys, so it can read credentials, tokens, and request bodies and forge every response.
Ship the same OTP upgrade for the same-day siblings CVE-2026-68956 (SSH idle session-channel memory DoS, CVSS 4.0 7.1 High) and CVE-2026-65634 (ASN.1 OBJECT IDENTIFIER decode CPU DoS during TLS cert parsing, CVSS 4.0 8.2 High). Do not open a second article for that train. This is the operator write-up; the HOL Guard evidence packs for CVE-2026-89422, CVE-2026-68956, and CVE-2026-65634 are the source records.
What breaks
On CVE-2026-89422, tls_client_connection_1_3:handle_server_hello/2 passes a received pre_shared_key extension into tls_gen_connection_1_3:handle_resumption/2, which sets resumption = true on presence alone without checking that the client offered a PSK. Meanwhile tls_handshake_1_3:get_pre_shared_key/4 falls back to the all-zero "no PSK" value and keys the handshake with the ordinary non-PSK schedule, so the attacker's own ephemeral key is enough. The resumption flag then routes maybe_resumption/1 straight to wait_finished, skipping the certificate-handling states.
The default recommended client config is in scope: {verify, verify_peer} with trusted cacerts, TLS 1.3 enabled and preferred, and default session_tickets = disabled. The session_tickets modes manual and auto are equally affected whenever the client has no ticket to offer, including every first connection to a host. Any consumer of ssl:connect that negotiates TLS 1.3 is exposed, including httpc over HTTPS, database and messaging client libraries, and TLS distribution clients.
Application-visible clues on an affected connection: ssl:peercert/1 returns {error, no_peercert} despite verify_peer, and ssl:connection_information/2 reports {session_resumption, true} for a client that never held a ticket.
On CVE-2026-68956, an authenticated SSH client can open many session channels that never get a shell, exec, or subsystem handler. Those idle channel records still land in the ETS channel cache, but max_channels only counts supervisor children, so the option does not bound the attack. One authenticated connection can grow memory until the emulator exits.
On CVE-2026-65634, the BER/PER/JER OBJECT IDENTIFIER decoders accumulate an unbounded base-128 subidentifier with quadratic work. A crafted OID (roughly 262 KB of continuation bytes in the advisory's example) can burn about 13 seconds of CPU on typical hardware. The decoder is generated into modules that parse X.509, including OTP-PUB-KEY via public_key:pkix_decode_cert/2, and it runs before signature or trust-chain verification. Default TLS clients (always parse the server cert) and mTLS servers (parse client certs) are exposed.
Who is not in scope
- TLS clients restricted to TLS 1.2 via
{versions, ['tlsv1.2']}are not affected by CVE-2026-89422 (they never hit the TLS 1.3 client path). - OTP builds already on 29.1.1, 28.5.0.7, or 27.3.4.18 (and later on those trains) are patched for this cluster.
- CVE-2026-68956 needs a successfully authenticated SSH session; it is not an unauthenticated internet worm. Network-restricted SSH daemons shrink exposure but do not replace the upgrade.
- CVE-2026-65634 is a CPU denial of service during cert/OID parse, not a certificate forgery or auth bypass by itself.
How to fix
Upgrade Erlang/OTP to one of:
OTP 29.1.1
OTP 28.5.0.7
OTP 27.3.4.18
Matching application versions called out by ERLEF:
- ssl: 11.7.7 / 11.6.0.6 / 11.2.12.13
- ssh: 6.0.6 / 5.5.2.6 / 5.2.11.13
- asn1: 5.5.2 / 5.4.3.1 / 5.3.4.3
Rebuild and redeploy every BEAM release, container image, and host package that embeds OTP (Elixir releases, RabbitMQ nodes, CouchDB, ejabberd, custom ssl:connect clients, and SSH daemons). There is no configuration that both keeps TLS 1.3 and mitigates CVE-2026-89422. The only temporary workaround is forcing TLS 1.2 on affected clients until you can patch.
For SSH while you roll: restrict who can authenticate, set a finite max_sessions, and rate-limit connections. Do not rely on max_channels for the idle-session bug.
One concrete check
erl -noshell -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().'
# and for the full patch level on modern OTP:
erl -noshell -eval '{ok, V} = file:read_file(filename:join([code:root_dir(), "releases", "RELEASES"])), io:format("~s~n",[V]), halt().'
Or check the package you actually ship:
rabbitmqctl status | head
# Elixir releases often embed OTP; inspect the release's erts / OTP version
cat _build/prod/rel/*/releases/*/OTP_VERSION 2>/dev/null || true
If you are below 27.3.4.18 / 28.5.0.7 / 29.1.1 on those trains, treat TLS 1.3 clients and ASN.1/TLS cert parse surfaces as exposed until the bump lands.
What this is not
This is not remote code execution, not an unauthenticated worm across the open internet, and not a bug that only hits exotic PSK ticket configs. It is a TLS 1.3 client authentication bypass under default settings, plus two High DoS siblings fixed on the same OTP patch train.
References
Continue reading
All posts
CVE-2026-81934: Redis TLS pending-list use-after-free (public RCE PoC)
How to fix CVE-2026-81934: upgrade Redis to 8.2.9, 8.4.6, 8.6.6, 8.8.2, or 8.10.1

Temporal write access can run shell on your Worker Service host
How to fix CVE-2026-89139: upgrade Temporal Server to 1.31.3 or 1.30.7

BREAKING: OpenShift console Devfile API lets anyone SSRF your cluster
How to fix CVE-2026-75885: upgrade OpenShift console when Red Hat ships the errata
