CVE-2026-49457: erlang/quic Broken TLS Certificate Verification (MITM)
Every erlang/quic client connection before version 1.4.4 skipped all three TLS server authentication checks, letting any attacker on the network path impersonate any server by presenting an arbitrary
The problem
The QUIC client in erlang/quic <= 1.4.3 performed zero server authentication during the TLS 1.3 handshake. Three distinct checks were absent: the CertificateVerify signature was never verified, the certificate chain was never validated against a trust store, and the hostname was never compared against the certificate subject or SANs.
As a result, the `verify` option was a no-op. An attacker with a position on the network path (LAN, rogue Wi-Fi, BGP hijack) could present any self-signed or unrelated certificate, complete the handshake, and fully decrypt and tamper with all application data.
HTTP/3 clients built on this library were equally exposed. CVSS 9.1 (Critical).
Proof of concept
A working proof-of-concept for CVE-2026-49457 in quic, with the exact payload below.
%% Attacker-controlled server: present ANY certificate (e.g. self-signed for evil.example)
%% Victim client on erlang/quic <= 1.4.3 -- 'verify' is a no-op, connection succeeds.
%% Victim code (typical usage, verify not set -- defaults to broken "on"):
{ok, Conn} = quic:connect("legitimate-bank.com", 443,
#{alpn => [<<"h3">>]},
self()).
%% Handshake completes even when MITM presents an unrelated cert.
%% 'verify => true' had identical behavior -- equally broken.
%% Attacker mitm server snippet (openssl s_server or any TLS terminator):
%% openssl req -x509 -newkey rsa:2048 -keyout evil.key -out evil.crt -days 1 -nodes \
%% -subj "/CN=evil.example"
%% openssl s_server -key evil.key -cert evil.crt -port 443 -quic
%% -> erlang/quic client accepts it for any hostname, no error.Three independent authentication steps required by TLS 1.3 (RFC 8446 §4.4.3, §4.4.2, §4.4.1) were simply never implemented in the client handshake path. The CertificateVerify digital signature was not checked, so a MITM could forward or fabricate a certificate without owning the corresponding private key.
The certificate chain was not walked against any trust store, and the resulting identity was never compared to the dialed hostname.
The patch in 1.4.4 added all three checks: crypto:verify of the CertificateVerify signature over the TLS 1.3 transcript hash, X.509 chain validation against the OS or user-supplied `cacerts` store, and RFC 6125 hostname matching. Client `verify` now defaults to enabled; the escape hatch is `verify => false` (for test servers with self-signed certs).
CWE-297 (Improper Validation of Certificate with Host Mismatch) and CWE-295 (Improper Certificate Validation) both apply.
The fix
Upgrade to erlang/quic 1.4.4 or later. No workaround exists for older versions -- `verify => true` had no effect before the patch. After upgrading, set `cacerts` in connect options if you need a custom CA bundle; leave it unset to use the OS trust store. Use `verify => false` only for isolated test environments with self-signed certificates.
Reported by benmmurphy.
Related research
- highCVE-2026-47074: ex_aws_sns SNS Signature Verification Bypass via Unvalidated SigningCertURL
- highCVE-2026-47066CVE-2026-47066: hackney Alt-Svc Parser Infinite Loop (DoS)
- highCVE-2026-47071CVE-2026-47071: hackney SOCKS5 TLS Upgrade Infinite Timeout
- highCVE-2026-47073CVE-2026-47073: erlang/hackney WebSocket Unbounded Memory Consumption