A constrained-IoT DTLS library reads a few bytes past a buffer while parsing a server certificate handshake message
Eclipse tinydtls is a small-footprint DTLS implementation designed for constrained IoT devices — think CoAP endpoints, Zephyr/RIOT/Contiki sensors, the kind of node where mbedTLS or OpenSSL are too heavy. The flaw lives in check_server_certificate(), the client-side routine that validates an ECDSA certificate received during the DTLS handshake (ServerCertificate/ServerKeyExchange). A malformed cert message lets the parser walk past the end of the input buffer, producing a CWE-125 out-of-bounds read. All commits before b3efd41 are affected; the fix is in tip-of-trunk.
There is no vendor CVSS for this CVE — tinydtls is an Eclipse research-grade library and the advisory does not carry a scored vector. An out-of-bounds *read* in a handshake-time cert parser is genuinely on the low-to-moderate end: it's reachable pre-auth on the client side, but the realistic impact is a crash (DoS of one IoT device) or, on platforms with side-channel-readable memory, a small information leak. There is no documented path to RCE, no heap corruption, and the affected role — a sensor or CoAP client talking to a malicious DTLS server — is a narrow, low-value target. *LOW* is the honest assessment.
3 steps from start to impact.
Attacker stands up a malicious DTLS server
openssl s_server -dtls or a custom scapy/tinydtls-fork harness that emits a crafted ServerCertificate.- Attacker controls a DTLS endpoint the client will connect to, OR has on-path position on the UDP flow
- Client is configured to use ECDHE-ECDSA cipher suite (the cert-bearing path)
- Most tinydtls deployments use PSK (pre-shared key), not raw-public-key/ECDSA — the vulnerable code path is never reached
- Devices typically only talk to a hardcoded backend over a known network path; arbitrary internet exposure is rare for constrained IoT
Send malformed ServerCertificate to trigger OOB read
ServerCertificate (or ServerKeyExchange) record whose length fields lie about the embedded ECDSA structure, so check_server_certificate() advances its parse pointer past the message boundary. Weaponization is a few dozen bytes of malformed ASN.1/raw-public-key payload — proof-of-concept territory, no published exploit.- The malicious server completes enough of the handshake to reach the certificate exchange step
- Client invokes the vulnerable parse routine
- Many embedded targets have MPU/MMU memory protection that will trap a read past a region boundary as a fault rather than leak
- Compiler hardening (
-fstack-protector,_FORTIFY_SOURCE) doesn't help here — this is heap/static OOB, not stack smash
Outcome: device crash or small information leak
- Attacker has a side channel to observe leaked bytes (timing, repeated handshakes with differing payloads)
- DTLS clients typically retry on handshake failure — repeated failures get noticed; flooding leaks is detectable
- Most embedded targets have no useful secrets adjacent to the parse buffer
syft or your firmware-SBOM tool against built images and grep for tinydtls / dtls.c checksum. This is hygiene — no mitigation SLA applies to a LOW verdict, so this can ride the standard 365-day remediation window.TLS_PSK_WITH_AES_128_CCM_8) does not invoke check_server_certificate() and is unaffected. If your fleet's threat model permits PSK, this eliminates exposure entirely. Apply during the next firmware revision.- WAF / network IDS — DTLS is encrypted UDP and the malformed payload lives inside the handshake; no L7 inspector parses this in flight.
- TLS interception / decryption proxies — they don't sit between IoT sensors and their backends in any meaningful enterprise deployment.
- Stack canaries / FORTIFY_SOURCE — this is heap/static OOB read, not stack smash; compiler hardening does not catch it.
- EDR — constrained IoT has no EDR, and even hosted variants don't introspect DTLS handshake parsers.
The supporting signals.
| In-the-wild exploitation | None known. No KEV entry, no public exploit, no IR firm has reported abuse. |
|---|---|
| Public PoC | No public weaponized PoC. Fix commit b3efd41 on the Eclipse tinydtls repo serves as the only technical reference. |
| EPSS | Not yet scored (CVE published 2026-06-29). Class-prior for CWE-125 client-side parsers is typically <0.5% in the first 90 days. |
| CISA KEV | Not listed. |
| CVSS | *No vendor/authority CVSS published.* Plausible CVSS:3.1 vector: AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:L ≈ 4.8 — reflecting unauth, network, but high attack complexity and limited impact. |
| Affected versions | All Eclipse tinydtls commits prior to b3efd41ad111a4920f599f51ffa4f5e9f1e72221. Library is unversioned in trunk; downstream distros (Zephyr modules, Contiki-NG, RIOT pkg) may pin older snapshots. |
| Fixed version | Eclipse tinydtls main branch at commit b3efd41 or later. No tagged release — vendor projects must re-vendor. |
| Exposure data | Shodan/Censys do not fingerprint tinydtls clients (clients don't listen). Server-side CoAPs deployments on UDP/5684 number in the low thousands globally per Shodan — but the *server* code path is not where this bug lives. |
| Disclosed | 2026-06-29 via Eclipse Foundation / GitHub Advisory Database. |
| Reporter | Not publicly credited in the advisory metadata at time of disclosure. |
Sources.
Why this verdict
- No write primitive: CWE-125 is read-only OOB. Without a corruption primitive, RCE chains require an additional bug that has not been published.
- Client-side trigger only: The attacker must lure the device into handshaking with malicious infrastructure. Most constrained IoT devices talk to one hardcoded backend over a controlled network path — exposure population is tiny.
- Dominant cipher-suite mode unaffected: tinydtls's flagship use case is PSK for sensor fleets; the vulnerable
check_server_certificate()is only invoked in ECDHE-ECDSA / raw-public-key mode, which is a minority configuration. - Role multiplier — low-value target: Affected role is a *constrained sensor / CoAP client*. Blast radius is a single device. Even on fleets, each handshake is independent — no lateral movement, no pivot.
- Role multiplier — no high-value role: tinydtls is explicitly *not* used in identity providers, hypervisors, PAM, CI/CD, backup, or kernel-mode agents. There is no canonical high-value deployment role to floor the verdict against.
- No KEV, no PoC, no campaigns: EPSS-class prior is <0.5%; nothing in the wild.
Why not higher?
MEDIUM/HIGH would require either a credible RCE chain (none published, and CWE-125 alone cannot produce one) or a high-value deployment role (tinydtls has none — it is a research-grade library for sensors). Network-reachable cert-parse bugs sound severe in OpenSSL or mbedTLS because of installed base; tinydtls's installed base is sensor firmware, not infrastructure.
Why not lower?
IGNORE is wrong because the bug *is* network-triggerable pre-auth, and a determined attacker on-path with a constrained-IoT client population could cause sustained DoS or harvest small memory fragments. SBOM-tracked firmware should still re-vendor at the next release cycle.
Crowdsourced verification payload.
Run on a Linux build host or CI runner that has the firmware source tree checked out. Invoke as ./check-tinydtls.sh /path/to/firmware/src (no privileges required). The script grep-walks for vendored tinydtls and compares its dtls.c against the fix commit.
#!/usr/bin/env bash
# check-tinydtls.sh — detect Eclipse tinydtls pre-b3efd41 in a source tree
# Usage: ./check-tinydtls.sh <path-to-source-tree>
# Exit: 0 PATCHED, 1 VULNERABLE, 2 UNKNOWN/NOT FOUND
set -u
ROOT="${1:-.}"
FIX_COMMIT="b3efd41ad111a4920f599f51ffa4f5e9f1e72221"
FIX_SHORT="b3efd41"
if [[ ! -d "$ROOT" ]]; then
echo "UNKNOWN: $ROOT is not a directory"
exit 2
fi
# Find candidate tinydtls trees (dtls.c + dtls.h colocated)
mapfile -t HITS < <(find "$ROOT" -type f -name dtls.c 2>/dev/null | while read -r f; do
d=$(dirname "$f")
[[ -f "$d/dtls.h" && -f "$d/peer.c" ]] && echo "$d"
done)
if [[ ${#HITS[@]} -eq 0 ]]; then
echo "UNKNOWN: no tinydtls source tree found under $ROOT"
exit 2
fi
STATUS=0
for d in "${HITS[@]}"; do
echo "[*] tinydtls candidate: $d"
if [[ -d "$d/.git" || -f "$d/.git" ]]; then
if git -C "$d" merge-base --is-ancestor "$FIX_COMMIT" HEAD 2>/dev/null; then
echo " PATCHED (fix $FIX_SHORT is ancestor of HEAD)"
else
echo " VULNERABLE (fix $FIX_SHORT not in history)"
STATUS=1
fi
else
# No git metadata — fall back to source-level fingerprint of the fix
if grep -qE 'check_server_certificate' "$d/dtls.c"; then
# Heuristic: the post-fix code adds an explicit length-bound check before ECDSA parse
if grep -qE 'data_length\s*<\s*sizeof.*ecdsa|bounds.*server.*cert' "$d/dtls.c"; then
echo " LIKELY PATCHED (bounds check present)"
else
echo " LIKELY VULNERABLE (no post-fix bounds check fingerprint)"
STATUS=1
fi
else
echo " UNKNOWN (check_server_certificate symbol missing — atypical fork)"
[[ $STATUS -eq 0 ]] && STATUS=2
fi
fi
done
case $STATUS in
0) echo "RESULT: PATCHED";;
1) echo "RESULT: VULNERABLE";;
2) echo "RESULT: UNKNOWN";;
esac
exit $STATUS