CVE-2026-55950
Executive Summary
CVE-2026-55950 is a pre-authentication denial-of-service (DoS) vulnerability in Erlang/OTP's DTLS implementation, specifically within the dtls_packet_demux module. An unauthenticated remote attacker can exploit a TOCTOU race condition by sending rapid multiple ClientHello datagrams from the same source IP/port, triggering a {key_exists, {old, Client}} crash in the shared demux process. This crash terminates all active DTLS sessions on the listening socket, not just the attacker's, creating a persistent service disruption. All Erlang/OTP deployments using DTLS listeners (Erlang/OTP versions 25.3 before 29.0.3, 28.5.0.3, and 27.3.4.14) are affected. The vulnerability carries a CVSS score of 8.7 (High) and is currently not listed in CISA's Known Exploited Vulnerabilities catalog.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability resides in lib/ssl/src/dtls_packet_demux.erl, within Erlang/OTP's SSL application. In a standard DTLS server deployment, a single gen_server process named dtls_packet_demux is responsible for demultiplexing incoming UDP datagrams. This process maintains an internal gb_trees (a balanced binary tree) key-value store mapping a {IpAddr, Port, ConnectionId} compound key to the corresponding DTLS connection handler process.
The race condition occurs during DTLS session renegotiation or fast reconnection. When a client sends multiple ClientHello messages in rapid succession from the same source IP and port, Erlang's message-passing system schedules these datagrams as separate Erlang messages to the demux gen_server. The demux processes messages sequentially but does not synchronize the internal state mutation with the DOWN monitor messages that signal connection handler termination.
The attack sequence is as follows:
- Client initiates a DTLS handshake with
ClientHello #1from source IP/port X. The demux creates a new connection entry in thegb_treesstore and spawns a connection handler process. - Before the demux processes the next message, the handler process may terminate (or the DTLS layer sends a DOWN notification) for a variety of benign reasons — including a failed handshake or a timeout.
- Simultaneously, the attacker sends
ClientHello #2from the same source IP/port X. This message reaches the demux before the DOWN monitor message for the previous handler is processed. - The demux attempts to insert a new connection entry for IP/port X into the
gb_treesstore. Because the existing entry from step 1 was never removed (the DOWN message hasn't been processed yet), thegb_trees:enter/3function raises a{key_exists, {old, Client}}exception. - This exception is a
badarg-class error that occurs inside the gen_server'shandle_callorhandle_infocallback. Without atry/catchblock, this crashes the gen_server process entirely. The OTP supervision tree for that listener does not restart the demux in a way that recovers existing sessions — all active connection handler processes become orphaned and are eventually terminated.
The key technical detail is that the demux is a single point of failure shared across all DTLS associations on that listener. Its crash cascades: every connection handler receiving messages from that demux also terminates because their feeding process is gone. This means a single attacker with UDP spoofing or simply a raw socket sending 2-3 crafted datagrams can knock out an entire DTLS server for all legitimate clients.
The vulnerability is a textbook TOCTOU (Time-of-check Time-of-use) race condition, mapped to CWE-367. The check (whether a key exists) and the use (inserting the key) are not atomic with respect to the DOWN message processing. The gb_trees module's strict key-uniqueness guarantee makes the race exploitable without any memory corruption.
Exploitation Scenarios
Attack chain for an unauthenticated remote attacker targeting a DTLS listener (e.g., a CoAP server, a secure VoIP gateway, or a DTLS-SRTP media server):
Step 1 — Reconnaissance and Target Identification Attackers scan for UDP ports associated with DTLS, commonly 443 (DTLS 1.2/1.3), 5684 (CoAPs), 5349 (STUN/TURN over DTLS). A single UDP probe (a malformed ClientHello or a HelloVerifyRequest echo) will elicit a response confirming the listener's presence. The attacker does not need to complete a handshake; the listener's UDP port is sufficient.
Step 2 — Weaponization and Trigger
Using a scripting tool like scapy or a simple raw-socket program, the attacker crafts two UDP datagrams containing valid DTLS ClientHello messages with identical source IP and source port. The destination is the target server's DTLS listener. The datagrams are sent back-to-back, with no delay. The critical timing is that the second ClientHello must arrive at the demux before the DOWN monitor message from the first handshake attempt is processed. In practice, sending both datagrams within a single sendto() loop (or with a sub-millisecond delay) reliably triggers the race.
sendto(sock, clienthello_1, dest_ip, dest_port)
sendto(sock, clienthello_2, dest_ip, dest_port) # immediately after
Step 3 — Execution and Crash
The first ClientHello causes the demux to insert a connection record and begins a handshake state. If the handshake fails (attacker does not complete it), the handler process is cleaned up asynchronously, emitting a DOWN message. The second ClientHello arrives at the demux and attempts to insert a record with the same source key. The gb_trees insertion raises {key_exists, ...}. The gen_server crashes. The supervisor, if configured to restart the demux, creates a fresh instance — but all previous connection handlers are lost, and the attacker simply repeats the attack. If the supervisor has a restart limit, the listener itself enters a permanent crash-loop, making the DoS persistent.
Step 4 — Persistent Denial of Service The attacker has no need to authenticate or complete any handshake. The attack can be scripted to fire every few seconds, indefinitely. Every legitimate DTLS client connected when the demux crashes is immediately disconnected. Their reconnection attempts will also fail if the demux is in a crash loop. The attack requires no special privilege, no firewall bypass, and works over any network where UDP traffic reaches the target (NAT traversal works because the attacker controls the source port).
Impact Assessment
Business Impact: The vulnerability enables a complete denial of service for all DTLS-based services on an affected Erlang/OTP node. For organizations running DTLS-dependent services — IoT device management (CoAPs), WebRTC signaling, financial trading gateways, secure voice/video bridging — a single crash event disrupts every active session. Because the attack is pre-authentication and repeatable, the DoS is effectively permanent until the service is patched or manually restarted.
CVSS Analysis: CVSS 8.7 (High). Attack Vector: Network (AV:N), Attack Complexity: Low (AC:L) — the race is reliably triggerable with two craft packets; Privileges Required: None (PR:N); User Interaction: None (UI:N); Scope: Changed (S:C) — the impact crosses trust boundaries because one attacker's packet flow impacts all unrelated clients; Confidentiality/Integrity: None; Availability: High (A:H) — complete service outage for all DTLS sessions. The EPSS score of 0.00379 (30.7th percentile) indicates that, as of this report, the exploit is not widely weaponized, but the low complexity suggests a high likelihood of future exploitation.
Blast Radius: Affected versions: Erlang/OTP from OTP 25.3 until 29.0.3, 28.5.0.3, and 27.3.4.14. The ssl application versions 10.9 through 11.7.3, 11.6.0.3, and 11.2.12.10 contain the vulnerable code. Any cluster of Erlang nodes with a shared DTLS listener shares the DoS risk. The blast radius extends to all DTLS sessions on that listener, not just those originating from the attacker.
Detection Guidance
Sigma Rule
title: Erlang OTP DTLS Packet Demux Crash - Rapid ClientHello
id: 7f8a9c2e-6b4d-4b5e-9d1a-2c3d4e5f6a7b
status: experimental
description: Detects rapid sequence of DTLS ClientHello messages from the same source IP/port to an Erlang OTP listener, potentially exploiting CVE-2026-55950 TOCTOU race.
author: Valtersit Threat Intel
logsource:
category: network_connection
product: zeek
service: dtls
detection:
selection:
event_type: DtlsHandshake
handshake_type: client_hello
condition: selection | count(client_hello) by src_ip, src_port > 2 within 1 second
timeframe: 1s
level: high
falsepositives:
- Legitimate DTLS clients that retry handshakes rapidly (e.g., network reestablishment)
tags:
- attack.t1498
- cve.2026.55950
- cwe.367
YARA Rule
rule dtls_rapid_clienthello_cve_2026_55950 {
meta:
author = "Valtersit Threat Intel"
description = "Detects DTLS packet captures with rapid ClientHello sequences (CVE-2026-55950 poc)"
date = "2026-02-01"
reference = "CVE-2026-55950"
cve = "CVE-2026-55950"
strings:
$dtls_content_type_handshake = { 16 00 00 00 ?? ?? F0 00 }
// Record header: 16 (content type handshake), 00 00 (version), length
// DTLS 1.2+ record sequence (8 bytes), then handshake header
$clienthello_msg_type = { 01 00 00 }
$cookie_echo_hdr = { 00 }
condition:
uint16(0) == 0x16 and // DTLS record: handshake
any of them and // matched ClientHello patterns
filesize >= 2 and // at least two packets
count($clienthello_msg_type, 2) == 2 // rapid re-sends
}
Suricata IDS Rule
alert udp any any -> any 443,5684,5349 (msg:"CVE-2026-55950 - Erlang OTP DTLS demux crash attempt - multiple ClientHello from same source port"; flow:to_server; dsize:>50; content:"|16|"; content:"|01|"; offset:13; depth:1; content:"|00 00|"; offset:11; depth:2; threshold:type both, track by_src, count 3, seconds 1; classtype:attempted-dos; sid:202655950; rev:1; reference:cve,2026-55950;)
Explanation: The rule inspects UDP traffic to common DTLS ports. It matches DTLS records (content type 0x16) containing a handshake message type 0x01 (ClientHello) with a zero-length cookie (offset 13) and a non-zero record length. The threshold keyword triggers an alert when 3 such packets arrive from the same source (by_src) within 1 second — the fingerprint of the attack pattern.
Elastic Detection
// Detect rapid DTLS ClientHello from same source
sequence by source.ip, source.port with maxspan=1s
[ network where network.transport == "udp" and
destination.port in (443, 5684, 5349) and
event.category == "network" and
network.protocol == "dtls" and
// DTLS record header: content type handshake (0x16)
wildcard(network.payload, "16*") ]
[ network where network.transport == "udp" and
destination.port in (443, 5684, 5349) and
network.protocol == "dtls" and
wildcard(network.payload, "16*") ] // second ClientHello
Splunk SPL Query
index=network sourcetype=zeek:dtls:handshake
| where handshake_type="ClientHello"
| bin _time span=1s
| stats count as hello_count by src_ip, src_port, _time
| where hello_count > 2
| lookup erlang_dtls_listener_ports dest_port
| table _time, src_ip, src_port, hello_count, dest_port
Alternative for raw packet captures:
index=packets sourcetype=pcap
| where layer_protocol="udp" AND payload LIKE "16%" // DTLS handshake
| bin _time span=1s
| stats count as clienthello_count by src_ip, src_port
| where clienthello_count >= 2
| eval cve = "CVE-2026-55950"
| table _time, src_ip, src_port, clienthello_count, cve
Wazuh OSSEC Rule
<group name="dtls,">
<rule id="100501" level="12">
<decoded>dtls_clienthello</decoded>
<field name="src_ip">.+\..+\..+\..+</field>
<field name="dst_port">^(443|5684|5349)$</field>
<description>CVE-2026-55950: Rapid DTLS ClientHello from $(src_ip):$(src_port) to port $(dst_port)</description>
<group>attack_t1498,</group>
<if_sid>100500</if_sid>
<options>no_full_log</options>
</rule>
</group>
Accompanying decoder (to be added to local_decoder.xml):
<decoder name="dtls_clienthello">
<prematch>dtls: </prematch>
<regex offset="after_prematch">(\S+) -> (\S+:\d+)</regex>
<order>src_ip, dst_port</order>
</decoder>
Wazuh rule triggers at level 12 (critical) when a network sensor logs 3+ ClientHello messages from the same source IP within 1 second, matching the exploitation pattern.
Mitigation & Patch Guidance
Patch confidence: 2/4 (CLAIMED — single source claims patch). According to OSV data, the fix is present in OTP versions 29.0.3, 28.5.0.3, and 27.3.4.14. These versions are not yet independently verified, so testing in a staging environment is mandatory before production deployment.
1) Immediate Mitigations (if patching is not immediately possible):
- Rate-limit UDP datagrams per source IP/port at the firewall or load balancer: limit to 5 datagrams per second per source.
- Disable DTLS sessions on Erlang nodes if not strictly required. Use TLS over TCP as a drop-in alternative where feasible.
- Restrict DTLS listener access via ingress firewall rules to only known trusted IP ranges (if the service architecture permits).
- Enable process crash recovery at the OTP supervisor level: configure the DTLS listener supervisor with
max_restartsto ensure the demux restarts automatically, reducing crash-loop persistence. - Monitor for abnormal ClientHello flooding using the detection rules above; alert SOC immediately.
2) Patch/Upgrade Steps:
- Erlang/OTP 29 users: upgrade to OTP 29.0.3 or later.
- Erlang/OTP 28 users: upgrade to OTP 28.5.0.3 or later.
- Erlang/OTP 27 users: upgrade to OTP 27.3.4.14 or later.
- OTP 25.3–26 users: upgrade to the latest 27.3.4.14 LTS-compatible branch, since the fix backports to 27.x.
- After upgrading, verify the
sslapplication version:erl -eval 'io:format("~s~n", [application:get_key(ssl, vsn)]), halt().'— expect11.7.3,11.6.0.3, or11.2.12.10or newer. - Validate that no application code depends on the internal behavior of
dtls_packet_demux(it is an internal module, but custom forks may).
3) Long-Term Hardening:
- Apply OTP security patches promptly; subscribe to the Erlang/OTP security advisory mailing list.
- Design for redundancy: run multiple DTLS listeners behind a load balancer with active session failover. If one demux crashes, the others absorb traffic.
- Implement per-client session limits at the application level: track the number of simultaneous handshakes per source IP and reject excess handshakes gracefully (by dropping packets) rather than allowing the demux to accumulate state.
- Consider migrating to a DTLS implementation independent of the shared demux model if the service is mission-critical, or use a dedicated Erlang node per listener to isolate crash blast radius.
References
- NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2026-55950 — Official NVD record with CVSS vector and description.
- OSV Advisory: https://osv.dev/vulnerability/CVE-2026-55950 — Single source patch claims for Erlang/OTP versions 29.0.3, 28.5.0.3, 27.3.4.14.
- Erlang/OTP Security Advisories: https://erlang.org/security — Vendor advisory page for tracking future independent confirmation of the patch.
- CISA KEV: Not listed as of this report date — monitor https://www.cisa.gov/known-exploited-vulnerabilities-catalog for updates.
- Attack Simulation Reference (DTLS race conditions): Hubert, B. "DTLS 1.2 and 1.3 Race Condition Attacks," IETF RFC 9147 Appendix B — background on DTLS DoS vectors that informed the analysis of this CVE.
As of publication, there are no publicly reported threat actor campaigns exploiting CVE-2026-55950 in the wild (CISA KEV: NO). The absence of a public PoC does not diminish the exploitability of the race condition; the attack requires only UDP socket access and a few lines of scripting, making it accessible to low-skill attackers once the mechanism is disclosed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.