Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-48859

CVE-2026-48859: Erlang/OTP SSH Timing Attack Vulnerability

CVE-2026-48859 is a timing side-channel vulnerability in Erlang/OTP SSH that enables unauthenticated username enumeration. Attackers can distinguish valid from invalid usernames via observable timing differences in authentication.

Published:

CVE-2026-48859 Overview

CVE-2026-48859 is an observable timing discrepancy vulnerability in the Erlang/OTP SSH implementation. The flaw resides in the ssh_auth and ssh_options modules and enables unauthenticated remote username enumeration through a timing side-channel in password authentication.

When the SSH daemon is configured with the user_passwords or password option, ssh_auth:check_password/3 runs a PBKDF2-SHA256 computation with 600,000 iterations (approximately 300ms) for valid usernames. Invalid usernames return immediately via the ssh_options:get_password_option/2 path. A single authentication attempt is sufficient to distinguish valid from invalid accounts. The issue affects OTP 29.0 before 29.0.2, corresponding to the ssh application 6.0 before 6.0.1.

Critical Impact

Unauthenticated remote attackers can enumerate valid SSH usernames using a single network probe, enabling targeted password attacks and credential stuffing campaigns against Erlang/OTP SSH servers.

Affected Products

  • Erlang/OTP 29.0 before 29.0.2
  • Erlang/OTP ssh application 6.0 before 6.0.1
  • SSH daemons configured with the user_passwords or password options

Discovery Timeline

  • 2026-06-10 - CVE-2026-48859 published to NVD
  • 2026-06-10 - Last updated in NVD database

Technical Details for CVE-2026-48859

Vulnerability Analysis

The vulnerability is a classic timing side-channel that violates the principle of constant-time authentication responses. The Erlang/OTP SSH server takes two distinct code paths when handling a password authentication request, depending on whether the supplied username exists in the configured user_passwords or password option.

For a known username, the server retrieves the stored salted password digest and performs a PBKDF2-SHA256 derivation with 600,000 iterations before comparing the result. This computation takes roughly 300 milliseconds on commodity hardware. For an unknown username, the server returns failure almost immediately, taking close to 0 milliseconds.

The two-orders-of-magnitude latency gap is trivially observable over the network. An attacker measuring response time of a single SSH password authentication exchange can determine whether a username exists. This maps to [CWE-208] Observable Timing Discrepancy.

Root Cause

The root cause is the absence of a constant-time fallback. The valid-user branch performs an expensive key derivation function, while the invalid-user branch in ssh_options:get_password_option/2 short-circuits without performing equivalent work. The user_passwords and password options are documented as intended for test purposes only, but production deployments commonly use them anyway.

Attack Vector

An unauthenticated remote attacker initiates an SSH connection and submits a password authentication request for a candidate username. The attacker measures the elapsed time between sending the SSH_MSG_USERAUTH_REQUEST and receiving the failure response. Times near 300ms indicate a valid username; sub-millisecond responses indicate the account does not exist. The attack requires no credentials and works against any reachable SSH listener using the affected configuration.

text
                 Checker when is_function(Checker, 1) ->
                     {Checker(Password), Ssh};
                 _ ->
+                    %% Run fake PBKDF2 to prevent timing oracle (GHSA-3w6p-vwhf-wvp4)
+                    _ = (?GET_INTERNAL_OPT(fake_passwd_checker, Opts))(Password),
                     {false, Ssh}
             end;

Source: GitHub Commit c342092. The patch invokes a fake_passwd_checker function on the invalid-user path so both branches perform equivalent PBKDF2 work and return in comparable time.

Detection Methods for CVE-2026-48859

Indicators of Compromise

  • High volumes of failed SSH password authentication attempts originating from a small set of source IP addresses, each targeting a unique username.
  • SSH session logs showing repeated userauth_request messages with password method and no successful authentications.
  • Sequential username probing patterns drawn from common dictionaries (admin, root, ubuntu, service account names).
  • Spikes in short-lived TCP connections to port 22 on Erlang/OTP SSH listeners.

Detection Strategies

  • Correlate SSH authentication failures by source IP and username to identify enumeration sweeps rather than isolated brute-force attempts.
  • Inspect SSH server logs for authentication requests against non-existent usernames; a burst of such requests is suggestive of timing probing.
  • Deploy network detection rules that flag SSH clients which open a connection, submit one password attempt, and disconnect.

Monitoring Recommendations

  • Forward sshd and Erlang/OTP SSH application logs to a centralized SIEM for cross-host correlation.
  • Track per-username failure rates and alert when failures span many distinct usernames from one source.
  • Monitor latency distributions of authentication responses to detect timing measurement campaigns.

How to Mitigate CVE-2026-48859

Immediate Actions Required

  • Upgrade Erlang/OTP to 29.0.2 or later, which includes the ssh 6.0.1 fix that equalizes authentication timing.
  • Replace user_passwords and password configuration options with the pwdfun callback, which is not affected by this vulnerability.
  • Restrict SSH listener exposure to trusted networks using firewall rules or bastion hosts while patching is in progress.
  • Audit existing SSH configurations across all Erlang/OTP nodes to inventory which deployments use the affected options.

Patch Information

The fix is committed in erlang/otp commit c342092. The patch introduces a fake_passwd_checker that runs a PBKDF2 computation against the supplied password whenever the username is invalid, ensuring both branches consume equivalent CPU time. Refer to the GitHub Security Advisory GHSA-3w6p-vwhf-wvp4 and the CNA advisory CVE-2026-48859 for full vendor guidance.

Workarounds

  • Migrate authentication to the pwdfun callback, which performs caller-controlled verification logic outside the vulnerable code paths.
  • Disable password authentication entirely and require public key authentication via the auth_methods option.
  • Place affected SSH services behind a VPN or jump host to limit unauthenticated network reach.
bash
# Replace user_passwords/password with pwdfun in your SSH daemon start options
ssh:daemon(22, [
    {system_dir, "/etc/ssh"},
    {pwdfun, fun(User, Password, _PeerAddress, _State) ->
        my_auth_module:verify(User, Password)
    end},
    {auth_methods, "publickey,password"}
]).

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.