← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-34875 · CWE-120 · Disclosed 2026-04-01

Mbed TLS through 3

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is less like a front-door break-in and more like a sharp edge hidden inside a seldom-used maintenance hatch

CVE-2026-34875 is a buffer overflow in psa_export_public_key() when handling FFDH public keys in Mbed TLS 3.5.0 through 3.6.5 and TF-PSA-Crypto 1.0.0. If an application exports an FFDH public key into a caller-provided buffer that is too small, the library can overwrite memory instead of returning the documented buffer-too-small error. Fixed upstream versions are Mbed TLS 3.6.6+ and TF-PSA-Crypto 1.1.0+.

The vendor-style 9.8/CRITICAL network-RCE framing overshoots reality for most enterprises. The bug is real and memory corruption always deserves respect, but this is not a generic TLS handshake parser bug and not a broadly reachable daemon flaw by default; an attacker needs a deployment that actually exposes a code path exporting an attacker-influenced FFDH key, plus an application integration that passes an undersized buffer. That stack of prerequisites is why this lands as MEDIUM, not CRITICAL.

"Serious memory corruption bug, but the real-world blast radius is cut down by a rare, non-default export path."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find a reachable FFDH export path

The attacker first needs an application that uses Mbed TLS or TF-PSA-Crypto and exposes a path that eventually calls psa_export_public_key() for FFDH keys. This is typically a bespoke API, device-management workflow, crypto service, or firmware feature rather than a normal TLS listener. A practical weapon would be a bespoke client or API harness, not a stock internet exploit kit.
Conditions required:
  • Target runs Mbed TLS 3.5.0-3.6.5 or TF-PSA-Crypto 1.0.0
  • Application actually uses FFDH key material
  • An attacker can trigger public-key export through a reachable interface
Where this breaks in practice:
  • FFDH export is a niche path compared with normal TLS handshakes
  • Many deployments use ECDHE/X25519 and never touch this code path
  • Library presence alone does not imply exploitability
Detection/coverage: SCA/SBOM tools should flag the vulnerable library version, but network scanners will miss most exploitable instances because reachability depends on application logic, not a banner.
STEP 02

Control an export of an attacker-influenced key

The advisory says code execution may be possible if an attacker can cause the application to export an arbitrary FFDH key. In practice, that means the attacker must influence which key is exported or supply/import key material that later gets exported. A likely weapon is a custom protocol exerciser against import/generate/export flows, not a single-packet trigger.
Conditions required:
  • Application allows attacker-controlled or attacker-selectable FFDH key operations
  • Export policy or workflow permits the key to be exported
Where this breaks in practice:
  • Many key stores and secure-element designs do not expose export at all
  • Key usage policy may block export or copy operations
  • This often implies authenticated or semi-trusted workflow access, even if the CVSS vector says PR:N
Detection/coverage: Look for unusual sequences of key import/generation followed by export requests in application logs, HSM/SE logs, or API telemetry. Generic IDS signatures are unlikely to be useful.
STEP 03

Hit the undersized output buffer

The actual flaw occurs because the library writes the full FFDH public key even when the caller-provided buffer is too small, instead of failing with PSA_ERROR_BUFFER_TOO_SMALL as the PSA API documentation describes. Exploitation therefore depends on the surrounding application allocating the wrong size and then exposing that faulty path to attacker influence. A likely exploit primitive is a memory-corruption harness tailored to the target build, allocator, and architecture.
Conditions required:
  • Caller allocates too-small output buffer for the exported key
  • Target build and runtime memory layout make the overwrite useful
Where this breaks in practice:
  • Well-written callers use PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE() or PSA_EXPORT_PUBLIC_KEY_MAX_SIZE and avoid the bug
  • Modern hardening, watchdogs, and memory protections may turn this into a crash instead of code execution
  • Embedded targets vary heavily by compiler, ABI, and allocator, complicating reliable exploitation
Detection/coverage: Crash telemetry, watchdog resets, sanitizer builds, and fuzzing are more useful than perimeter monitoring here. EDR coverage is often absent or thin on the embedded/IoT platforms where Mbed TLS is common.
STEP 04

Convert corruption into impact

If the prior steps line up, the result could be denial of service, unstable crypto operations, or in the best case for the attacker, code execution inside the calling process. But the exploit has to be shaped for a very specific app-level integration, which sharply limits mass exploitation. This is a targeted exploit-development problem, not an opportunistic internet spray issue.
Conditions required:
  • Corrupted memory lands on a controllable object or return path
  • The target process has meaningful privileges or operational importance
Where this breaks in practice:
  • Reliability is build-specific
  • Blast radius is usually the single process or device using the vulnerable export path
  • No current evidence of broad in-the-wild weaponization
Detection/coverage: Post-crash forensic evidence may be sparse on appliances and microcontrollers. Runtime anomaly monitoring on the calling application is your best bet where available.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed exploitation found in the sources reviewed; the CISA KEV catalog does not list CVE-2026-34875.
Proof-of-concept availabilityNo public PoC located in vendor, NVD, Debian, Ubuntu, or GitHub release/advisory sources reviewed. That does not prove none exists, but there is no obvious commodity exploit trail.
EPSS0.00057 from your intel block — very low predicted near-term exploitation probability.
KEV statusNot KEV-listed as of the CISA KEV catalog page reviewed; no date added applies.
CVSS vector reality checkCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H assumes a directly reachable network trigger. The vendor advisory instead describes an FFDH public-key export API bug, which is materially narrower than a generic exposed service flaw.
Affected versionsMbed TLS 3.5.0–3.6.5 and TF-PSA-Crypto 1.0.0 per the upstream advisory.
Fixed versionsMbed TLS 3.6.6+ and TF-PSA-Crypto 1.1.0+ upstream. Debian tracker shows sid fixed at 3.6.6-0.1; Ubuntu currently marks affected packages as Needs evaluation and its OSV page shows Ubuntu priority medium.
Scanner/exposure dataInternet-wide scan data is low-value for this CVE. This is a library-internal export path, not a distinct bannerable service; finding Mbed TLS on the wire does not prove the vulnerable function is reachable. That is an inference from the advisory plus the PSA API call semantics.
Disclosure date2026-04-01 in NVD / user intel; the upstream advisory is dated 2026-03-31.
Reporter / creditsCredited upstream to Haruto Kimura (Stella).
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.8/10)

The decisive downgrade factor is reachability: this bug lives in an FFDH public-key export path, not in a broadly exposed handshake parser or default listener. Most enterprises with Mbed TLS deployed will have the vulnerable code present on disk, but only a much smaller subset will expose an attacker-reachable workflow that both uses FFDH and mishandles export buffer sizing.

HIGH Affected and fixed version boundaries
MEDIUM Real-world exploitability downgrade versus vendor CVSS
LOW Exact exposed-population estimate across embedded and custom application estates

Why this verdict

  • Vendor 9.8 assumes a socket to the bug. The actual flaw is in psa_export_public_key() for FFDH keys, so I am cutting severity because this is not equivalent to unauthenticated packet-level reachability on every service linked against Mbed TLS.
  • Reachability penalty: requires FFDH export, not just library presence. The target must use FFDH and expose a workflow that exports the public key; that is a strong narrowing factor in real deployments.
  • Caller-integration penalty: exploitation needs an undersized application buffer. Well-implemented callers using PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE() or PSA_EXPORT_PUBLIC_KEY_MAX_SIZE should avoid the dangerous state entirely, which pushes this away from mass-exploitable territory.
  • Telemetry is cold. Your EPSS is 0.00057, there is no KEV listing, and I found no obvious public PoC in primary-source review, so there is no evidence-based reason to keep this in the CRITICAL bucket.

Why not higher?

Because the exploit chain is application-specific and non-default. This is not a wormable listener bug, not a routine TLS handshake parsing issue, and not something a scanner can reliably prove reachable from the network just because the library exists. The vendor's network/PR:N framing describes theoretical impact, not typical enterprise exposure.

Why not lower?

Because the underlying bug is still memory corruption in a cryptographic library, and the vendor explicitly states arbitrary code execution may be possible if an attacker can drive export of an arbitrary FFDH key. If you run custom crypto services, embedded management planes, or device firmware that exposes PSA key workflows, the impact can still be ugly on the subset that is actually reachable.

05 · Compensating Control

What to do — in priority order.

  1. Inventory every Mbed TLS and TF-PSA-Crypto consumer — Use SBOM, package inventory, firmware BOMs, and source-code search to find where psa_export_public_key() and FFDH are actually used. For a MEDIUM finding there is no mitigation SLA — go straight to the 365-day remediation window, but do the inventory early because the hard part here is exposure mapping, not patch mechanics.
  2. Disable or avoid FFDH export paths — Where you own the application, remove or gate FFDH public-key export features, and prefer modern curves that do not touch this code path. There is no mitigation SLA for MEDIUM, so use this as a targeted hardening step where patching is delayed or the software is embedded in longer-lived devices.
  3. Enforce safe PSA buffer sizing — Review callers to ensure they allocate export buffers with PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE() or PSA_EXPORT_PUBLIC_KEY_MAX_SIZE, exactly as the PSA API documents. This is the best temporary engineering control for custom apps while you work toward the 365-day remediation deadline.
  4. Restrict key export operations — Tighten authorization around any management/API workflow that can generate, import, or export asymmetric keys, especially on appliances and device-control planes. Even without a mitigation SLA, shrinking who can reach export operations materially reduces exploitability in the interim.
  5. Fuzz and crash-test custom integrations — If you build products on top of Mbed TLS, run targeted fuzzing and negative tests around FFDH key export and key-management APIs. This does not replace patching, but it helps you separate mere library presence from actually exploitable integrations before the remediation window closes.
What doesn't work
  • A WAF will not save you unless the vulnerable path happens to sit behind an HTTP API you fully understand; the bug is in application-side memory handling, not a recognizable web payload pattern.
  • Perimeter network scanning does not tell you whether the export path is reachable. Seeing Mbed TLS in a binary or on a banner is not the same as proving exploitable exposure for this CVE.
  • MFA is irrelevant when the vulnerable workflow is unauthenticated, and only partially helpful when the real issue is a flawed internal export path after auth.
06 · Verification

Crowdsourced verification payload.

Run this on the target host, firmware build root, or extracted container/image filesystem where the library is installed. Invoke it as python3 check_cve_2026_34875.py / or against an offline root like python3 check_cve_2026_34875.py /mnt/rootfs; no admin rights are required beyond read access to package metadata and header files.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# CVE-2026-34875 checker for Mbed TLS / TF-PSA-Crypto
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

import os
import re
import sys
import subprocess
from pathlib import Path

ROOT = Path(sys.argv[1] if len(sys.argv) > 1 else "/")

MBED_VULN_MIN = (3, 5, 0)
MBED_PATCHED = (3, 6, 6)
TF_VULN_EXACT = (1, 0, 0)
TF_PATCHED = (1, 1, 0)

results = []
notes = []


def norm_root(p):
    p = str(p)
    if ROOT == Path("/"):
        return p
    return str(ROOT / p.lstrip("/"))


def parse_semver(text):
    m = re.search(r'(\d+)\.(\d+)\.(\d+)', text or '')
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def cmp_ver(a, b):
    return (a > b) - (a < b)


def assess_mbed(version_text, source):
    ver = parse_semver(version_text)
    if not ver:
        results.append(("UNKNOWN", f"Mbed TLS version unreadable from {source}: {version_text}"))
        return

    # Distro/backport ambiguity: if package version has a distro suffix and upstream part is old,
    # the package may contain a backport not visible from the plain semver.
    if re.search(r'(ubuntu|deb|el\d|amzn|alpine|sles|fc\d)', version_text, re.I):
        results.append(("UNKNOWN", f"Mbed TLS distro package version needs vendor advisory validation: {source}={version_text}"))
        return

    if cmp_ver(ver, MBED_VULN_MIN) >= 0 and cmp_ver(ver, MBED_PATCHED) < 0:
        results.append(("VULNERABLE", f"Mbed TLS vulnerable version from {source}: {version_text}"))
    elif cmp_ver(ver, MBED_PATCHED) >= 0:
        results.append(("PATCHED", f"Mbed TLS patched version from {source}: {version_text}"))
    else:
        results.append(("PATCHED", f"Mbed TLS version older than affected range from {source}: {version_text}"))


def assess_tf(version_text, source):
    ver = parse_semver(version_text)
    if not ver:
        results.append(("UNKNOWN", f"TF-PSA-Crypto version unreadable from {source}: {version_text}"))
        return

    if re.search(r'(ubuntu|deb|el\d|amzn|alpine|sles|fc\d)', version_text, re.I):
        results.append(("UNKNOWN", f"TF-PSA-Crypto distro package version needs vendor advisory validation: {source}={version_text}"))
        return

    if ver == TF_VULN_EXACT:
        results.append(("VULNERABLE", f"TF-PSA-Crypto vulnerable version from {source}: {version_text}"))
    elif cmp_ver(ver, TF_PATCHED) >= 0:
        results.append(("PATCHED", f"TF-PSA-Crypto patched version from {source}: {version_text}"))
    else:
        results.append(("UNKNOWN", f"TF-PSA-Crypto unusual version from {source}: {version_text}"))


def read_file(path):
    try:
        with open(path, 'r', encoding='utf-8', errors='ignore') as f:
            return f.read()
    except Exception:
        return None


def check_headers():
    header_candidates = [
        norm_root('/usr/include/mbedtls/version.h'),
        norm_root('/usr/local/include/mbedtls/version.h'),
        norm_root('/opt/include/mbedtls/version.h'),
        norm_root('/include/mbedtls/version.h'),
    ]
    tf_candidates = [
        norm_root('/usr/include/tf-psa-crypto/version.h'),
        norm_root('/usr/local/include/tf-psa-crypto/version.h'),
        norm_root('/opt/include/tf-psa-crypto/version.h'),
        norm_root('/include/tf-psa-crypto/version.h'),
    ]

    for p in header_candidates:
        if os.path.isfile(p):
            data = read_file(p)
            if data:
                m = re.search(r'MBEDTLS_VERSION_STRING\s+"([^"]+)"', data)
                if m:
                    assess_mbed(m.group(1), p)
                    return True
    for p in tf_candidates:
        if os.path.isfile(p):
            data = read_file(p)
            if data:
                m = re.search(r'TF_PSA_CRYPTO_VERSION_STRING\s+"([^"]+)"', data)
                if m:
                    assess_tf(m.group(1), p)
                    return True
    return False


def run_cmd(cmd):
    try:
        out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
        return out.strip()
    except Exception:
        return None


def check_pkg_config():
    if ROOT != Path('/'):
        return False
    hit = False
    for name in ['mbedtls', 'tf-psa-crypto']:
        out = run_cmd(['pkg-config', '--modversion', name])
        if out:
            hit = True
            if name == 'mbedtls':
                assess_mbed(out, f'pkg-config:{name}')
            else:
                assess_tf(out, f'pkg-config:{name}')
    return hit


def check_dpkg_status():
    status_path = norm_root('/var/lib/dpkg/status')
    if not os.path.isfile(status_path):
        return False
    data = read_file(status_path)
    if not data:
        return False
    hit = False
    for block in data.split('\n\n'):
        pkg = re.search(r'^Package:\s*(.+)$', block, re.M)
        ver = re.search(r'^Version:\s*(.+)$', block, re.M)
        if not pkg or not ver:
            continue
        name = pkg.group(1).strip()
        version = ver.group(1).strip()
        if name in ('libmbedtls-dev', 'libmbedtls21', 'libmbedtls14', 'libmbedtls12', 'libmbedcrypto16', 'libmbedcrypto7', 'libmbedcrypto3', 'mbedtls'):
            hit = True
            assess_mbed(version, f'dpkg:{name}')
        elif name in ('tf-psa-crypto', 'libtf-psa-crypto-dev'):
            hit = True
            assess_tf(version, f'dpkg:{name}')
    return hit


def check_apk_installed():
    path = norm_root('/lib/apk/db/installed')
    if not os.path.isfile(path):
        return False
    data = read_file(path)
    if not data:
        return False
    hit = False
    name = None
    ver = None
    for line in data.splitlines() + ['']:
        if line.startswith('P:'):
            name = line[2:].strip()
        elif line.startswith('V:'):
            ver = line[2:].strip()
        elif line == '':
            if name and ver:
                if 'mbedtls' in name:
                    hit = True
                    assess_mbed(ver, f'apk:{name}')
                elif 'tf-psa-crypto' in name:
                    hit = True
                    assess_tf(ver, f'apk:{name}')
            name = None
            ver = None
    return hit


def check_rpm_db():
    if ROOT != Path('/'):
        return False
    out = run_cmd(['rpm', '-qa', '--qf', '%{NAME} %{VERSION}-%{RELEASE}\n'])
    if not out:
        return False
    hit = False
    for line in out.splitlines():
        parts = line.split(None, 1)
        if len(parts) != 2:
            continue
        name, version = parts
        if 'mbedtls' in name:
            hit = True
            assess_mbed(version, f'rpm:{name}')
        elif 'tf-psa-crypto' in name:
            hit = True
            assess_tf(version, f'rpm:{name}')
    return hit


def summarize():
    if not results:
        print('UNKNOWN')
        print('No Mbed TLS or TF-PSA-Crypto evidence found in common package DBs or headers.')
        sys.exit(2)

    for status, msg in results:
        notes.append(f'[{status}] {msg}')

    has_vuln = any(status == 'VULNERABLE' for status, _ in results)
    has_patched = any(status == 'PATCHED' for status, _ in results)
    has_unknown = any(status == 'UNKNOWN' for status, _ in results)

    if has_vuln:
        print('VULNERABLE')
        for n in notes:
            print(n)
        sys.exit(1)
    if has_patched and not has_unknown:
        print('PATCHED')
        for n in notes:
            print(n)
        sys.exit(0)
    print('UNKNOWN')
    for n in notes:
        print(n)
    sys.exit(2)


if __name__ == '__main__':
    found = False
    found |= check_pkg_config()
    found |= check_dpkg_status()
    found |= check_apk_installed()
    found |= check_rpm_db()
    found |= check_headers()
    summarize()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not treat this like a fleet-wide emergency RCE just because the raw CVSS says 9.8. First, identify where your estate actually exposes FFDH key export or custom PSA key-management workflows; for a MEDIUM reassessment there is no noisgate mitigation SLA — go straight to the 365-day remediation window unless your own product team confirms an externally reachable export path, in which case harden or disable that workflow immediately. Then patch upstream consumers to Mbed TLS 3.6.6+ or TF-PSA-Crypto 1.1.0+ within the noisgate remediation SLA of 365 days, prioritizing internet-facing appliances, custom crypto services, and embedded management planes first this quarter rather than burning the whole patch program this week.

Sources

  1. Mbed TLS advisory: CVE-2026-34875
  2. NVD entry
  3. PSA Crypto API key export documentation
  4. Mbed TLS 3.6.6 release
  5. TF-PSA-Crypto changelog showing 1.1.0 branch release
  6. Debian security tracker
  7. Ubuntu CVE page
  8. CISA Known Exploited Vulnerabilities catalog
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.