← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
tenable:311420 · CWE-346 · Disclosed 2026-04-29

libcurl 7

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

This is a mislabeled spare key, not a broken front door

CVE-2026-6276 is a libcurl-only cookie scope bug affecting versions 7.71.0 through 8.19.0 and fixed in 8.20.0. If an application reuses the same libcurl *easy handle*, sends one HTTP request with a custom Host: header, then sends a later request on that same handle without the custom header, libcurl can keep stale cookie-host state and forward cookies from host A to host B. Upstream explicitly says the curl command-line tool is not affected.

Tenable and NVD label this HIGH/7.5, but that does not match reality. Upstream curl rates it Low because the exploit chain is narrow: you need a vulnerable app pattern, cookie handling enabled, handle reuse, a custom Host: header transition, and in practice the bug is most relevant to cleartext HTTP or otherwise contrived test/proxy setups; HTTPS plus correct SNI sharply reduces reachable abuse.

"Real bug, tiny reach: this is an app-specific cookie leak, not a fleet-wide fire drill"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach a libcurl-using app path

The attacker needs an application that embeds libcurl and exposes a request flow they can influence. The public reference is the vendor advisory plus the public HackerOne report #3671818, but this is not a one-packet internet exploit; it is an application-behavior bug inside a caller's request logic.
Conditions required:
  • Target software actually uses libcurl, not just the curl CLI
  • Attacker can influence outbound HTTP requests or target URLs
  • Application enables cookie handling
Where this breaks in practice:
  • Many hosts with libcurl installed never expose attacker-controlled request flows
  • A local version hit from Nessus does not prove exploitability in the application
  • The curl CLI is unaffected, removing a huge chunk of assumed exposure
Detection/coverage: Version scanners catch presence only. Nessus plugin 311420 is a local, version-based check and states it did not test the vulnerable code path.
STEP 02

Trigger the stale host state

The attacker must hit a code path that uses the same easy handle for multiple HTTP requests, sets a custom Host: header on request 1, and then omits it on request 2. That sequence is the entire bug; without that exact reuse pattern, cookies do not spill.
Conditions required:
  • Application reuses one libcurl easy handle across requests
  • Request 1 sets a custom Host: header
  • Request 2 reuses the handle after that header is removed
Where this breaks in practice:
  • This is a very specific programming pattern, often seen in debugging, proxying, or bespoke integrations
  • Many applications create fresh handles or never touch custom Host: at all
  • Modern SDK wrappers often abstract this away and avoid the sequence
Detection/coverage: Static code review or telemetry around CURLOPT_HTTPHEADER and handle reuse can find risky callers; network scanners cannot.
STEP 03

Get cookies forwarded cross-host

Once the stale cookiehost state exists, libcurl can attach cookies from the first host to the second host on the next request. The bug leaks confidentiality only; it does not grant code execution, privilege escalation, or direct service compromise.
Conditions required:
  • Relevant cookies exist for the first host
  • Second request is attacker-observable or attacker-controlled
  • Application does not isolate cookie jars per origin/workflow
Where this breaks in practice:
  • If no sensitive cookies are present, the bug is mostly noise
  • If the second host is not attacker-controlled or observable, impact drops
  • Many enterprise service accounts use tokens outside browser-style cookie workflows
Detection/coverage: Look for unexpected Cookie: headers on outbound requests to unrelated hosts in proxy logs or app-layer tracing.
STEP 04

Monetize the leak

The attacker still has to do something useful with the captured cookie: replay a session, pivot into another application, or extract tenant-scoped data. That makes this a post-bug, app-specific abuse problem rather than a universal host compromise event.
Conditions required:
  • Leaked cookie remains valid
  • Cookie maps to a security-relevant session
  • Downstream app accepts replay from attacker infrastructure
Where this breaks in practice:
  • Session binding, short TTLs, or IP/device checks can blunt reuse
  • Many leaked cookies would only impact one app or one tenant context
  • Blast radius is usually limited to the affected request workflow
Detection/coverage: App auth logs may show replay from unusual source IPs, but there is no universal IOC for the library flaw itself.
03 · Intelligence Metadata

The supporting signals.

In the wildNo public evidence of active exploitation found in source checks, and no CISA KEV listing was found for CVE-2026-6276.
PoC availabilityA public HackerOne report (#3671818) is referenced by curl and NVD; Tenable marks Exploit Available: true, but this is best understood as a reproducible report/PoC, not a mass-exploitation kit.
EPSSTenable shows EPSS 0.00008. Snyk shows 0.01% probability, 3rd percentile — exactly what you'd expect for a niche app-pattern leak.
KEV statusNot listed in the CISA Known Exploited Vulnerabilities catalog in source checks.
CVSS mismatchNVD/Tenable use 7.5 HIGH with CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H, but upstream curl's own advisory marks the issue Low. The generic CVSS vector overstates reality by ignoring the app-specific preconditions.
Affected versionsUpstream affected range is 7.71.0 through 8.19.0 inclusive. Upstream unaffected are <7.71.0 and >=8.20.0. curl CLI is not affected.
Fixed versionsUpstream fix is 8.20.0. Distro backports exist: Ubuntu fixed in 7.81.0-1ubuntu1.24 for 22.04 LTS and 8.5.0-2ubuntu10.9 for 24.04 LTS; SUSE shipped fixes in SUSE-SU-2026:1717-1. Amazon Linux showed Pending Fix in the checked advisory.
Exposure/scanning realityThis is not internet-fingerprintable like a server daemon bug. Shodan/Censys-style counts are poor risk signals here because libcurl is a client library embedded in apps. Detection is mostly local inventory + code-path analysis.
Scanner coverageNessus plugin 311420 is local only and states it relies on the application's self-reported version rather than exercising the vulnerable behavior.
Timeline / creditReported to curl on 2026-04-14, distros notified 2026-04-23, advisory and fixed release published 2026-04-29, NVD published 2026-05-13. Reported by Muhamad Arga Reksapati; patch by Daniel Stenberg.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (2.9/10)

The decisive factor is exploit precondition stacking: an attacker needs a very specific libcurl caller pattern involving cookie use, Host: header manipulation, and easy-handle reuse. That sharply narrows the reachable population and keeps this far away from the kind of broadly exploitable remote issue that deserves a fleet-wide HIGH.

HIGH Severity downgrade from Tenable/NVD HIGH to **LOW**
MEDIUM Estimated exposure population inside typical enterprise app estates

Why this verdict

  • Baseline was inflated: the upstream curl project rates this issue Low, while NVD/Tenable's generic 7.5 HIGH assumes a much broader attack surface than the real bug has.
  • Requires app-specific misuse: attacker success depends on a caller reusing the same easy handle, enabling cookies, setting a custom Host: header on one request, and then removing it on the next. Each prerequisite compounds downward pressure on severity.
  • Reachable population is small: upstream says custom Host: is mostly used for debugging and the flaw primarily matters for cleartext HTTP; HTTPS requires correct SNI, which blocks the easy, generic abuse path.

Why not higher?

This is not unauthenticated remote code execution, not a wormable service flaw, and not a one-shot internet exploit against every host running libcurl. You need a very particular application behavior to even reach the bug, and the impact is cookie disclosure rather than host compromise.

Why not lower?

It still breaks an origin boundary and can leak real session material when the wrong app pattern exists. In bespoke integrations, reverse proxies, test harnesses, or internal middleware that juggle custom Host: headers, the bug can expose credentials or tenant-scoped sessions, so it is not pure informational noise.

05 · Compensating Control

What to do — in priority order.

  1. Ban custom Host: reuse — Audit application code and wrapper libraries for CURLOPT_HTTPHEADER patterns that set a custom Host: header and then reuse the same easy handle for later requests. For a LOW verdict there is no mitigation SLA; treat this as backlog hygiene and fold the audit into the normal remediation cycle.
  2. Isolate cookie state per workflow — Do not share one easy handle and cookie jar across unrelated destinations. Separate handles or cookie engines by origin so stale state cannot cross-pollinate requests. For a LOW verdict there is no mitigation SLA; make this an engineering hardening task rather than an emergency change.
  3. Prefer strict HTTPS flows — Where the affected integration still uses cleartext HTTP or synthetic host-header routing tricks, move it to normal HTTPS with correct hostname validation and SNI. That removes the easiest practical path described by upstream. For a LOW verdict there is no mitigation SLA; schedule it as hardening work.
  4. Triage by exploitability, not package count — Prioritize only apps that both use libcurl cookies and manipulate custom Host: headers. Ten thousand version hits on workstations or servers do not equal ten thousand exploitable paths. For a LOW verdict there is no mitigation SLA; use this to suppress queue noise.
What doesn't work
  • A WAF does not reliably help because the bug happens in the client library's outbound request handling, not at your inbound edge.
  • Perimeter internet exposure counts do not help much because this is a library flaw embedded in apps, not a directly fingerprintable listening service.
  • Blindly flagging every libcurl < 8.20.0 asset as urgent creates patch fatigue; many installs will never hit the required cookie + custom-Host + handle-reuse path.
06 · Verification

Crowdsourced verification payload.

Run this on the target host where the vulnerable application lives, not from an auditor workstation. Save as check_libcurl_cve_2026_6276.py and run python3 check_libcurl_cve_2026_6276.py; no admin rights are required, but the result may be UNKNOWN on distro-packaged builds because vendors often backport fixes without changing the upstream libcurl version string.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_libcurl_cve_2026_6276.py
# Detect likely exposure to CVE-2026-6276 (libcurl stale Host header cookie leak)
# Exit codes:
#   0 = PATCHED
#   1 = VULNERABLE
#   2 = UNKNOWN

import ctypes
import ctypes.util
import os
import platform
import re
import subprocess
import sys

LOW = (7, 71, 0)
HIGH = (8, 20, 0)


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


def vstr(v):
    return '.'.join(str(x) for x in v) if v else 'unknown'


def in_vuln_range(v):
    return v is not None and LOW <= v < HIGH


def is_patched_upstream(v):
    return v is not None and (v < LOW or v >= HIGH)


def run_cmd(cmd):
    try:
        p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=5)
        return p.returncode, p.stdout.strip(), p.stderr.strip()
    except Exception:
        return 127, '', 'failed'


def get_libcurl_runtime_version():
    candidates = []
    found = ctypes.util.find_library('curl')
    if found:
        candidates.append(found)
    system = platform.system().lower()
    if system == 'windows':
        candidates += ['libcurl.dll', 'curl.dll']
    elif system == 'darwin':
        candidates += ['libcurl.4.dylib', 'libcurl.dylib']
    else:
        candidates += ['libcurl.so.4', 'libcurl.so']

    tried = []
    for name in candidates:
        if name in tried:
            continue
        tried.append(name)
        try:
            lib = ctypes.CDLL(name)
            lib.curl_version.restype = ctypes.c_char_p
            raw = lib.curl_version()
            if not raw:
                continue
            s = raw.decode(errors='ignore')
            m = re.search(r'libcurl/(\d+\.\d+\.\d+)', s)
            if m:
                return {'source': f'loaded library {name}', 'raw': s, 'version': parse_semver(m.group(1))}
            return {'source': f'loaded library {name}', 'raw': s, 'version': parse_semver(s)}
        except Exception:
            continue
    return None


def get_curl_cli_version():
    rc, out, _ = run_cmd(['curl', '--version'])
    if rc != 0 or not out:
        return None
    first = out.splitlines()[0]
    m = re.search(r'curl\s+(\d+\.\d+\.\d+)', first)
    if not m:
        return None
    return {'source': 'curl --version', 'raw': first, 'version': parse_semver(m.group(1))}


def get_package_evidence():
    evidence = []

    # dpkg families
    if shutil_which('dpkg-query'):
        for pkg in ['libcurl4', 'libcurl4t64', 'libcurl3-gnutls', 'libcurl3-nss', 'curl']:
            rc, out, _ = run_cmd(['dpkg-query', '-W', '-f=${Version}', pkg])
            if rc == 0 and out:
                evidence.append({'manager': 'dpkg', 'package': pkg, 'version_text': out})

    # rpm families
    if shutil_which('rpm'):
        for pkg in ['libcurl', 'curl']:
            rc, out, _ = run_cmd(['rpm', '-q', '--qf', '%{VERSION}-%{RELEASE}', pkg])
            if rc == 0 and out and 'not installed' not in out.lower():
                evidence.append({'manager': 'rpm', 'package': pkg, 'version_text': out})

    return evidence


def shutil_which(cmd):
    paths = os.environ.get('PATH', '').split(os.pathsep)
    exts = ['']
    if platform.system().lower() == 'windows':
        exts += os.environ.get('PATHEXT', '.EXE;.BAT;.CMD').split(';')
    for p in paths:
        for ext in exts:
            candidate = os.path.join(p, cmd + ext)
            if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
                return candidate
    return None


def looks_like_distro_backport(version_text):
    vt = version_text.lower()
    markers = ['ubuntu', 'deb', '.el', 'amzn', 'suse', 'fc', 'ph', 'alpine', 'amazon']
    return any(m in vt for m in markers) or '-' in version_text


def main():
    runtime = get_libcurl_runtime_version()
    cli = get_curl_cli_version()
    packages = get_package_evidence()

    evidence = [x for x in [runtime, cli] if x]

    # Prefer runtime library evidence if present.
    chosen = runtime or cli

    if not chosen:
        print('UNKNOWN - could not determine a libcurl runtime or curl CLI version on this host')
        sys.exit(2)

    ver = chosen.get('version')
    if ver is None:
        print(f"UNKNOWN - unable to parse version from {chosen.get('source')}: {chosen.get('raw')}")
        sys.exit(2)

    if is_patched_upstream(ver):
        print(f"PATCHED - {chosen.get('source')} reports upstream version {vstr(ver)} outside the vulnerable range 7.71.0-8.19.0")
        sys.exit(0)

    # Version falls in the upstream vulnerable range. Check for distro packaging, where backports are common.
    for pkg in packages:
        if looks_like_distro_backport(pkg['version_text']):
            print(f"UNKNOWN - runtime reports {vstr(ver)} in the upstream vulnerable range, but distro package {pkg['package']}={pkg['version_text']} may include a backported fix; verify against your vendor advisory")
            sys.exit(2)

    print(f"VULNERABLE - {chosen.get('source')} reports upstream version {vstr(ver)} in the vulnerable range 7.71.0-8.19.0 and no distro backport evidence was found")
    sys.exit(1)


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

If you remember one thing.

TL;DR
Monday morning, do not let plugin 311420 blow up your patch queue. First, identify the small subset of applications that actually use libcurl cookies plus custom Host: headers plus easy-handle reuse; for everyone else, document the downgrade and move on. For a LOW verdict there is no noisgate mitigation SLA and no noisgate remediation SLA — treat this as backlog hygiene, patch in the normal maintenance stream, and only fast-track the few bespoke apps whose code path really matches the advisory.

Sources

  1. Tenable Nessus Plugin 311420
  2. curl advisory CVE-2026-6276
  3. curl JSON metadata for CVE-2026-6276
  4. NVD CVE-2026-6276
  5. Ubuntu USN-8227-1 curl vulnerabilities
  6. SUSE-SU-2026:1717-1 curl security update
  7. Amazon Linux CVE-2026-6276 advisory status
  8. Snyk vulnerability entry with EPSS percentile
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.