← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-34876 · CWE-125 · Disclosed 2026-04-02

Mbed TLS 3

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

This is less a front-door break-in than a clerk overfilling a form and accidentally photocopying the next page

CVE-2026-34876 is an out-of-bounds read in mbedtls_ccm_finish() in Mbed TLS 3.1.0 through 3.6.5. The bug appears when software uses the *public multipart CCM API* and passes an oversized tag_len greater than 16, causing memcpy(tag, ctx->y, tag_len) to read past the 16-byte authentication buffer and leak adjacent CCM context data. Mbed TLS 3.6.6 fixes this; Mbed TLS 4.x had the same validation issue internally, but the function is not exposed through the supported public API there.

The vendor's HIGH/7.5 score reflects the theoretical CVSS view that an attacker may be remote and unauthenticated. In real enterprise deployments, that overstates the risk: the attacker still needs an application that exposes the raw multipart CCM interface to attacker-controlled input, while the unaffected one-shot CCM APIs and PSA/cipher wrappers cover a lot of normal usage. This is a real bug worth fixing, but it is *not* a generic pre-auth network break across everyone who links Mbed TLS.

"High on paper, medium in fleets: this is a narrow library misuse bug, not a broadly reachable internet break"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find a reachable Mbed TLS 3.x embedding

The attacker first needs a product or custom application built with Mbed TLS 3.1.0-3.6.5. The key detail is not just library presence, but a code path that actually uses multipart CCM rather than the one-shot APIs or PSA wrappers documented as unaffected.
Conditions required:
  • Target software embeds Mbed TLS 3.1.0-3.6.5
  • Target feature uses CCM mode
  • The embedding application exposes that feature to untrusted input
Where this breaks in practice:
  • Mbed TLS is a library, so internet exposure depends entirely on the parent application
  • Many products use TLS/X.509 paths, not raw multipart CCM
  • Vendor advisory states one-shot CCM APIs and PSA/cipher wrappers are not affected
Detection/coverage: SBOM/SCA scanners can flag vulnerable versions, but they cannot tell you whether multipart CCM is actually reachable.
STEP 02

Reach the raw multipart API path

The vulnerable primitive is mbedtls_ccm_finish(), which is only exploitable when the application allows attacker-influenced flow into the multipart CCM sequence. In practice that means a custom protocol handler, embedded product, or application wrapper that accepts or derives a malicious tag_len from untrusted input.
Conditions required:
  • Application directly uses the public multipart CCM API
  • Attacker can influence or set tag_len
  • Application does not validate tag_len before calling the library
Where this breaks in practice:
  • Most sane wrappers hardcode or negotiate tag length once, instead of letting users pick it arbitrarily
  • Modern secure coding review or fuzzing often catches attacker-controlled length fields near crypto code
  • The vulnerable function is not a typical externally exposed API surface
Detection/coverage: Static analysis and code review can find direct calls to mbedtls_ccm_finish() and mismatched mbedtls_ccm_set_lengths() usage; network scanners generally cannot.
STEP 03

Trigger oversized copy

With a tag_len above 16, the vulnerable code executes memcpy(tag, ctx->y, tag_len). That reads past ctx->y and discloses up to tag_len - 16 bytes of adjacent CCM context, including nonce/counter data, length fields, mode parameters, and potentially key-dependent block cipher state.
Conditions required:
  • tag_len exceeds 16
  • The call path reaches mbedtls_ccm_finish()
  • The application returns or otherwise exposes the resulting tag buffer
Where this breaks in practice:
  • Leak size is bounded and tied to adjacent structure layout
  • This is read-only disclosure, not memory corruption for write or control flow
  • If the application discards or masks the returned tag, exfiltration value drops sharply
Detection/coverage: Targeted unit tests, ASan/UBSan builds, and fuzzing around CCM tag handling should catch it; generic vuln scanners will mostly miss runtime exploitability.
STEP 04

Convert leaked bytes into useful data

The final impact depends on what sits next to ctx->y in the compiled structure and whether the attacker can repeat the read. In the best attacker case, repeated requests expose cryptographic context that helps session analysis or secret recovery; in many real deployments, it leaks only low-value state and stops there.
Conditions required:
  • Leaked bytes are returned to attacker-visible output
  • Adjacent memory contains sensitive context
  • Attacker can repeat the operation reliably
Where this breaks in practice:
  • No direct code execution or memory modification primitive
  • Per-adaptation blast radius is usually limited to the process or session using that CCM context
  • Exploit utility varies heavily by compiler, layout, and application behavior
Detection/coverage: Application-layer anomaly detection may see malformed CCM requests or repeated invalid tag lengths; there is no dependable internet-facing signature for the CVE itself.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo current evidence of active exploitation in reviewed sources. CISA KEV does not list this CVE, and the vendor advisory does not claim exploitation.
Proof-of-concept availabilityNo public PoC located in reviewed primary sources. That is an inference from the source set, not a guarantee that no private exploit exists.
EPSS0.00026 (~0.026%) from the user-supplied intel, which is very low and consistent with a narrow, hard-to-reach library flaw. Percentile was not provided in the source set reviewed here.
KEV statusNot listed in the CISA KEV catalog as of this review.
CVSS vector reality checkCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N assumes network reachability and attacker control are present. The *real* gating factor is that the application must expose the raw multipart CCM API with attacker-controlled tag_len.
Affected versionsMbed TLS 3.1.0 through 3.6.5 are affected upstream. Vendor notes that 4.x had the same validation problem internally, but mbedtls_ccm_finish() is not exposed via the public API there.
Fixed versionsUpstream fix is Mbed TLS 3.6.6. Debian tracking shows forky/sid fixed at 3.6.6-0.1, while bookworm/trixie remained vulnerable in the reviewed tracker snapshot.
Exposure populationNo reliable internet-wide host count: this is a library/API flaw, not a bannerable standalone service. Shodan/Censys-style host counting is low-signal here because exposure is determined by the embedding application.
Disclosure and reporterVendor advisory date is 2026-03-31; NVD published on 2026-04-02. Credit goes to Eva Crystal (0xiviel).
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.1/10)

The decisive downward pressure is application reachability: exploitation requires a product to expose the raw multipart CCM API with attacker-controlled tag_len, which sharply narrows the real-world population versus the vendor's network-reachable CVSS baseline. This is a legitimate confidentiality bug, but it is not a broadly wormable or one-request internet compromise class issue.

HIGH Version range and technical flaw description
MEDIUM Real-world exploitability downgrade from vendor HIGH
LOW Public PoC absence beyond reviewed sources

Why this verdict

  • Downgrade for attacker-position friction: vendor CVSS starts at unauthenticated remote, but in reality the attacker needs a product that *already* exposes raw multipart CCM semantics to untrusted input, which many deployments never do.
  • Downgrade for exposure fraction: this is a library bug, not an exposed appliance endpoint. The reachable population is the subset of Mbed TLS consumers using multipart CCM *and* passing attacker-controlled tag_len unsafely.
  • Downgrade for security-tool intercepts: SAST, fuzzing, secure wrappers, and ordinary application input validation are all modern controls that can stop the vulnerable call chain before exploitation.
  • Keep at MEDIUM, not LOW: if the bad call path exists, triggering it is easy and the leak can expose sensitive CCM context, including key-dependent cipher state.

Why not higher?

This is not a generic pre-auth compromise of every host with Mbed TLS installed. The exploit chain depends on multiple narrowing prerequisites: the vulnerable 3.x branch, actual multipart CCM usage, attacker influence over tag_len, and application behavior that returns leaked bytes usefully. There is also no evidence in the reviewed sources of KEV listing, active exploitation, or public weaponization.

Why not lower?

The bug is still a real memory disclosure in security-sensitive cryptographic code, not a theoretical lint finding. If you ship or run custom embedded software that directly uses mbedtls_ccm_finish() on attacker-shaped inputs, the vulnerability is straightforward to trigger and can leak cryptographic context that should never leave process memory.

05 · Compensating Control

What to do — in priority order.

  1. Block raw multipart CCM exposure — Do not expose the multipart CCM API to untrusted callers; route callers through one-shot CCM APIs or PSA/cipher wrappers instead. For a MEDIUM verdict there is no mitigation SLA, but this is the right engineering guardrail to apply as code is touched before the 365-day remediation window closes.
  2. Clamp tag_len at the boundary — Enforce CCM tag sizes of 4-16 bytes and even values only, and require mbedtls_ccm_finish() to use the same tag_len previously set by mbedtls_ccm_set_lengths(). For a MEDIUM verdict there is no mitigation SLA — go straight to the 365-day remediation window, but this check is a solid temporary control for products that cannot upgrade immediately.
  3. Inventory embedded copies — Use SBOM, package inventory, firmware BOMs, and source scans to find Mbed TLS 3.1.0-3.6.5 hiding inside appliances, containers, and statically linked binaries. This matters more than perimeter scanning because the affected component is usually buried inside another product.
  4. Add targeted fuzz tests — Fuzz multipart CCM setup and finish paths with malformed and oversized tag_len values in CI. That catches this bug class and future length-validation mistakes before deployment.
What doesn't work
  • A WAF does not fix this by itself, because the bug is inside application/library handling after the request has already been accepted and translated into a crypto call.
  • MFA is irrelevant unless the vulnerable feature is behind authentication *and* authentication fully prevents attacker access to the code path; the bug itself is not an identity problem.
  • Version-only internet scanning is insufficient because finding Mbed TLS in a package list does not tell you whether multipart CCM is reachable from untrusted input.
06 · Verification

Crowdsourced verification payload.

Run this on the target host or inside the target container/firmware build environment where Mbed TLS is installed. Invoke it as python3 check_cve_2026_34876.py for auto-detection, or python3 check_cve_2026_34876.py --version 3.6.5 / python3 check_cve_2026_34876.py --path /usr/lib/x86_64-linux-gnu/libmbedcrypto.so; no special privileges are required unless your library paths are restricted.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_cve_2026_34876.py
# Detect likely exposure to CVE-2026-34876 (Mbed TLS 3.1.0 through 3.6.5)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

import argparse
import os
import platform
import re
import shutil
import subprocess
import sys
from typing import Optional, Tuple

VULN_MIN = (3, 1, 0)
VULN_MAX_EXCL = (3, 6, 6)


def normalize_version(raw: str) -> Optional[Tuple[int, int, int]]:
    if not raw:
        return None
    m = re.search(r'(\d+)\.(\d+)\.(\d+)', raw)
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def classify(ver: Tuple[int, int, int]) -> str:
    if VULN_MIN <= ver < VULN_MAX_EXCL:
        return 'VULNERABLE'
    if ver >= (4, 0, 0):
        return 'PATCHED'
    if ver < VULN_MIN:
        return 'PATCHED'
    return 'UNKNOWN'


def run(cmd):
    try:
        p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
        if p.returncode == 0:
            return p.stdout.strip()
    except Exception:
        pass
    return ''


def try_pkg_config() -> Optional[str]:
    if shutil.which('pkg-config'):
        for name in ('mbedtls', 'mbedtls3', 'libmbedtls'):
            out = run(['pkg-config', '--modversion', name])
            if out:
                return out
    return None


def try_dpkg() -> Optional[str]:
    if shutil.which('dpkg-query'):
        pkgs = ['libmbedtls-dev', 'libmbedtls14', 'libmbedtls', 'mbedtls']
        for pkg in pkgs:
            out = run(['dpkg-query', '-W', '-f=${Version}', pkg])
            if out:
                return out
    return None


def try_rpm() -> Optional[str]:
    if shutil.which('rpm'):
        for pkg in ('mbedtls', 'mbedtls-devel', 'libmbedtls'):
            out = run(['rpm', '-q', '--qf', '%{VERSION}-%{RELEASE}', pkg])
            if out and 'not installed' not in out.lower():
                return out
    return None


def try_apk() -> Optional[str]:
    if shutil.which('apk'):
        out = run(['apk', 'info', '-e', 'mbedtls'])
        if out:
            out2 = run(['apk', 'info', '-v', 'mbedtls'])
            return out2 or out
    return None


def try_pacman() -> Optional[str]:
    if shutil.which('pacman'):
        out = run(['pacman', '-Q', 'mbedtls'])
        if out:
            return out
    return None


def try_brew() -> Optional[str]:
    if shutil.which('brew'):
        out = run(['brew', 'list', '--versions', 'mbedtls'])
        if out:
            return out
    return None


def try_strings(path: str) -> Optional[str]:
    if not path or not os.path.exists(path):
        return None
    if shutil.which('strings'):
        out = run(['strings', '-a', path])
        if out:
            for line in out.splitlines():
                if 'mbed TLS ' in line or 'Mbed TLS ' in line or 'mbedtls-' in line:
                    m = re.search(r'(\d+\.\d+\.\d+)', line)
                    if m:
                        return m.group(1)
    return None


def guess_library_paths():
    paths = []
    sysname = platform.system().lower()
    if sysname == 'linux':
        paths.extend([
            '/usr/lib/libmbedcrypto.so',
            '/usr/lib64/libmbedcrypto.so',
            '/usr/lib/x86_64-linux-gnu/libmbedcrypto.so',
            '/usr/local/lib/libmbedcrypto.so',
            '/lib/x86_64-linux-gnu/libmbedcrypto.so',
        ])
    elif sysname == 'darwin':
        paths.extend([
            '/opt/homebrew/lib/libmbedcrypto.dylib',
            '/usr/local/lib/libmbedcrypto.dylib',
        ])
    elif sysname == 'windows':
        paths.extend([
            r'C:\\Program Files\\Mbed TLS\\libmbedcrypto.dll',
            r'C:\\mbedtls\\libmbedcrypto.dll',
        ])
    return [p for p in paths if os.path.exists(p)]


def main():
    parser = argparse.ArgumentParser(description='Check likely exposure to CVE-2026-34876')
    parser.add_argument('--version', help='Explicit Mbed TLS version to evaluate, e.g. 3.6.5')
    parser.add_argument('--path', help='Path to libmbedcrypto library/binary to inspect with strings')
    args = parser.parse_args()

    evidence = []

    if args.version:
        evidence.append(('explicit', args.version))

    if args.path:
        v = try_strings(args.path)
        if v:
            evidence.append((args.path, v))

    for label, func in [
        ('pkg-config', try_pkg_config),
        ('dpkg', try_dpkg),
        ('rpm', try_rpm),
        ('apk', try_apk),
        ('pacman', try_pacman),
        ('brew', try_brew),
    ]:
        v = func()
        if v:
            evidence.append((label, v))

    for p in guess_library_paths():
        v = try_strings(p)
        if v:
            evidence.append((p, v))

    seen = []
    for src, raw in evidence:
        ver = normalize_version(raw)
        if ver:
            seen.append((src, raw, ver, classify(ver)))

    if not seen:
        print('UNKNOWN - could not determine installed Mbed TLS version or library path')
        sys.exit(2)

    # Prefer any vulnerable finding over patched, because fleets often have multiple copies.
    vuln = [x for x in seen if x[3] == 'VULNERABLE']
    if vuln:
        src, raw, ver, state = vuln[0]
        print(f'VULNERABLE - detected Mbed TLS {ver[0]}.{ver[1]}.{ver[2]} from {src} ({raw})')
        sys.exit(1)

    patched = [x for x in seen if x[3] == 'PATCHED']
    if patched:
        src, raw, ver, state = patched[0]
        print(f'PATCHED - detected Mbed TLS {ver[0]}.{ver[1]}.{ver[2]} from {src} ({raw})')
        sys.exit(0)

    print('UNKNOWN - version detected but could not classify confidently')
    sys.exit(2)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, treat this as a targeted library triage item, not an all-hands emergency. Use SBOM/package data this week to find Mbed TLS 3.1.0-3.6.5, then have engineering identify which products actually expose multipart CCM to untrusted input; for a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window, but if you do find an exposed multipart CCM path, clamp tag_len or route callers to unaffected one-shot APIs immediately while scheduling the upstream fix. Apply the actual vendor patch to 3.6.6 or later within the noisgate remediation SLA of 365 days.

Sources

  1. Mbed TLS vendor advisory for CVE-2026-34876
  2. NVD entry for CVE-2026-34876
  3. Mbed TLS 3.6.6 release
  4. Debian security tracker entry
  5. Debian source package status for mbedtls
  6. Ubuntu CVE search results for mbedtls
  7. CISA Known Exploited Vulnerabilities catalog
  8. FIRST EPSS overview and data access
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.