Like a rigged ATM that only steals from people who deposit exactly $135 in cash
CVE-2026-92392 is a use-after-free in curl/libcurl's HTTP/2 upload path, affecting versions 8.12.0 through 8.22.0. When a malicious HTTP/2 server receives an upload, it can pause the transfer by zeroing the flow-control window, return partial response data, then send an RST_STREAM frame with reason HTTP_1_1_REQUIRED or REFUSED_STREAM. This causes curl to free a struct Curl_easy object while the multi handle still holds a dangling pointer. Subsequent cleanup downloads attacker-controlled data into the freed memory, overwriting a function pointer. The researcher (Luna Tong at Anthropic, using an AI-assisted OSS scanner) proved this can be chained into remote code execution, though the payload window is only ~23 bytes and the upload must be approximately 135 KB. The fix is a 6-line reorder in multi_done() in lib/multi.c. Fixed upstream in 8.23.0, with backports to 8.20.2, 8.16.2, and 8.14.3.
The vendor rates this HIGH at 8.1 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H), and on paper that is defensible — AC:H already accounts for the heap-grooming difficulty. But the CVSS vector overstates real-world risk in one critical way: the attacker must be the HTTP/2 server the victim is uploading to (or MITM the connection with a valid TLS certificate). That is not 'no privileges required' in the intuitive sense — it presupposes the attacker controls infrastructure the victim trusts enough to upload data to. Combined with the 135 KB upload-size constraint, architecture-specific heap layout requirements, and the fact that most exploitation attempts will produce a crash rather than code execution, the effective severity is a notch below the vendor label. We downgrade to HIGH 6.8 rather than MEDIUM because curl is the most widely deployed HTTP client library on the planet and the RCE is proven, not theoretical.
5 steps from start to impact.
Victim initiates HTTP/2 upload to attacker-controlled server
PUT or POST upload of approximately 135 KB to a server the attacker controls or has compromised. This can occur via CI/CD artifact pushes, API integrations, webhook deliveries, or any automated upload workflow. The server must present a valid TLS certificate for the target hostname.- Victim uses curl/libcurl 8.12.0–8.22.0
- HTTP/2 is negotiated (requires nghttp2 compiled in)
- Victim uploads ~135 KB of data to attacker-controlled endpoint
- Attacker must control or compromise the upload target server — this is not a drive-by attack
- Most enterprise upload targets are internal artifact stores, not arbitrary internet servers
- Certificate validation means the attacker needs a cert for the exact hostname the victim trusts
Server pauses upload and returns partial response
- Server implements custom HTTP/2 frame-level manipulation
- Timing must be precise to hit the vulnerable code path
- Requires a custom HTTP/2 server implementation — standard web servers cannot produce this sequence
- Timing sensitivity means network jitter can break the chain
Server sends RST_STREAM to trigger use-after-free
RST_STREAM with reason HTTP_1_1_REQUIRED or REFUSED_STREAM. This causes curl to free the struct Curl_easy but leave a dangling pointer in the multi handle. The freed memory region is now available for reallocation.- RST_STREAM must arrive after the upload is paused but before full cleanup
- The specific reason codes HTTP_1_1_REQUIRED or REFUSED_STREAM must be used
- If the frame arrives too early or too late, curl handles it correctly or simply crashes without exploitable state
heap-use-after-free diagnostics.Attacker-controlled data overwrites freed memory
struct Curl_easy memory region. The attacker crafts this data to overwrite a function pointer within the structure. The payload is limited to approximately 23 bytes, requiring precise knowledge of the target's architecture, libc allocator behavior, and heap layout.- Attacker knows target architecture (x86_64, aarch64, etc.)
- Heap layout must be favorable — depends on libc version, TLS library, and application-specific allocations
- Payload must fit in ~23 bytes
- Heap layout varies dramatically across Linux distributions, libc versions (glibc, musl), TLS backends (OpenSSL, GnuTLS, wolfSSL), and application embedding patterns
- ASLR and stack canaries further complicate reliable exploitation
- The 23-byte payload constraint severely limits what can be achieved — most real attacks will crash the process
multi_done() or nearby functions is a strong indicator.Code execution on victim client
- All prior heap-grooming conditions must align
- Function pointer must be called before any integrity check catches the corruption
- Daniel Stenberg's advisory explicitly states 'the most likely outcome of an attempted attack is rather just a crash'
- The attacker gets one shot per upload — a crash terminates the process and alerts monitoring
- Proven in lab conditions but no known successful weaponization in the wild
CURLOPT_HTTP_VERSION to CURL_HTTP_VERSION_1_1 or pass --http1.1 on the CLI for all upload (PUT/POST with body) operations. This completely eliminates the attack surface because the vulnerability is exclusively in the HTTP/2 RST_STREAM handling path. Deploy within 30 days per the noisgate mitigation SLA for HIGH. Audit automation scripts, CI/CD configs, and application code that uses libcurl for uploads.8.14.1-2+deb13u7, Ubuntu 26.04 8.18.0-1ubuntu2.8. Debian bookworm and Ubuntu ≤24.04 ship curl <8.12.0 and are not affected. For container images, rebuild base images with the updated package. Deploy within 180 days per the noisgate remediation SLA for HIGH.- WAF/IPS in front of the victim's outbound connections — this is a client-side vulnerability triggered by server responses, not by malicious requests. Traditional WAF rules inspect inbound traffic to servers and have no visibility into HTTP/2 frame-level responses received by a curl client.
- Disabling HTTP/2 at the network level (e.g., ALPN stripping) — while theoretically this works, ALPN stripping on a TLS-inspecting proxy breaks legitimate HTTP/2 traffic site-wide and is operationally destructive. Use the per-client
CURLOPT_HTTP_VERSIONapproach instead. - Certificate pinning alone — pinning prevents MITM but does not help if the attacker has compromised the legitimate upload target server itself. It is additive but not sufficient.
The supporting signals.
| In-the-wild exploitation | None observed. Not listed in CISA KEV as of 2026-10-16. No campaigns, no GreyNoise tags, no threat-intel reports of active exploitation. Daniel Stenberg's advisory and blog post confirm no known attacks. |
|---|---|
| Proof-of-concept availability | Defensive checker only. ashu7ank/CVE-2026-92392 on GitHub is a Python-based exposure scanner (0 stars, 9 commits) — it explicitly contains *no shellcode, no memory manipulation, no RCE payload*. It performs passive version checks and a loopback HTTP/2 protocol probe. Listed on pocindex.io via GitHub source only. No Nuclei template, no ExploitDB entry, no Metasploit module. Anthropic's internal PoC (used to prove RCE to the curl team) has not been published. |
| EPSS | 0.00226 (0.226%) — bottom quartile. Indicates extremely low predicted probability of exploitation in the next 30 days. Consistent with the high complexity and client-side attack model. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H = 8.1 HIGH. The AC:H reflects the heap-grooming difficulty. PR:N is technically correct (no auth on the victim side) but misleading — the attacker must control the server. UI:N is debatable since an upload must be initiated. |
| Affected versions | curl/libcurl 8.12.0 through 8.22.0. Introduced by commit cd9107e0128. Branch-specific affected ranges: 8.12.0–8.14.2, 8.15.0–8.16.1, 8.17.0–8.20.1, 8.21.0–8.22.0. |
| Fixed versions | Upstream 8.23.0 (2026-10-14). Maintenance backports: 8.14.3, 8.16.2, 8.20.2. Debian trixie: 8.14.1-2+deb13u7 (DSA-6558-1). Ubuntu 26.04: 8.18.0-1ubuntu2.8 (USN-8927-1). Debian bookworm (7.88.x) and Ubuntu 24.04/22.04 are not affected — the vulnerable code was introduced in 8.12.0. |
| Scanning/exposure data | No GreyNoise, Shodan, or Censys scanning activity associated with this CVE. This is expected — it is a client-side vulnerability, not a remotely scannable server flaw. Internet-facing exposure metrics do not apply in the traditional sense. |
| Disclosure timeline | Reported 2026-09-15 by Luna Tong (Anthropic OSS Scanner). Fixed and disclosed 2026-10-14 with curl 8.23.0 release. 29-day coordinated disclosure window. Patch authored by Stefan Eissing. |
| Researcher/discovery | Luna Tong, Anthropic — discovered using Anthropic's opt-in AI-assisted OSS Scanner (code-named AISLE). Of the 24 CVEs fixed in curl 8.23.0, AI-assisted research found 20, including this one — the only HIGH. First HIGH-severity curl flaw since the SOCKS5 heap overflow (CVE-2023-38545) in October 2023. |
| HTTP/2 requirement | Vulnerability requires nghttp2 to be compiled into curl. Curl builds without HTTP/2 support (no --with-nghttp2 at compile time, or Features: line missing HTTP2) are not affected regardless of version. |
Sources.
Why this verdict
- Client-side attack model is the dominant friction: The attacker must control or compromise the HTTP/2 server the victim uploads to, or perform a MITM with a valid TLS certificate. This is not a remotely exploitable server flaw — it inverts the typical attack direction. The CVSS
PR:Nis technically correct but functionally misleading; the attacker needs an infrastructure position, not merely network access. - Heap-grooming constraints compound beyond AC:H: The ~135 KB upload size requirement, architecture-specific payload, 23-byte payload limit, and libc/TLS-backend-dependent heap layout mean that a working exploit must be tailored per-target-environment. Daniel Stenberg explicitly states most attempts will crash the process rather than achieve RCE. The CVSS
AC:Hpartially captures this, but the real-world difficulty exceeds what a single complexity toggle can express. - No weaponized PoC, no wild exploitation, rock-bottom EPSS: The only public repository is a defensive scanner with zero offensive capability. Anthropic's proof-of-RCE has not been released. EPSS at 0.226% and no KEV listing confirm the threat-intel community sees minimal near-term exploitation probability.
- Role multiplier: curl/libcurl is present on virtually every Linux host, macOS system, and container image. *Low-value role (workstations, dev machines):* exploitation requires the developer to upload ~135 KB to a malicious server over HTTP/2 — low probability, blast radius is one workstation. *Typical role (application servers making API calls):* most API calls are downloads/GETs, not uploads of the right size — low probability. *High-value role (CI/CD runners uploading artifacts):* if a CI/CD pipeline uploads build artifacts to a compromised artifact store, the attacker achieves code execution on the runner, potentially pivoting to supply-chain compromise. CI/CD runners represent >1% of curl's installed base, and the outcome is supply-chain-scale. This sets the verdict floor at HIGH. However, the chain requires the artifact store to already be compromised (a separate, severe breach), and the heap-grooming requirements make reliable exploitation unlikely even then.
- Net assessment: The vendor's 8.1 overstates real-world risk because CVSS cannot encode 'attacker must be the server you voluntarily upload to.' We downgrade to 6.8 but hold at HIGH due to the CI/CD supply-chain floor and the proven (if difficult) RCE chain in the world's most deployed HTTP library.
Why not higher?
A CRITICAL rating would require either reliable exploitation without extraordinary preconditions, active exploitation in the wild, or a weaponized public PoC. None of these apply. The 23-byte payload limit, 135 KB upload-size constraint, environment-specific heap layout requirements, and the fact that crashes far outnumber successful exploits all argue against CRITICAL. The attack also requires the victim to upload to an attacker-controlled server, which is a fundamentally harder attacker position than most CRITICAL-rated CVEs demand.
Why not lower?
Despite the substantial friction, a proven use-after-free → RCE chain in curl/libcurl — the HTTP client library embedded in billions of devices, every major Linux distribution, every container base image, and most CI/CD pipelines — cannot be rated MEDIUM. The supply-chain implications of RCE in CI/CD upload paths set a HIGH floor. The vulnerability class (UAF with function-pointer overwrite) is well-understood by sophisticated attackers, and the constraint space, while narrow, is enumerable. History shows that 'too hard to exploit' curl bugs (cf. CVE-2023-38545 SOCKS5 overflow) attract sustained research attention once disclosed.
Crowdsourced verification payload.
Run this script on each target host where curl or libcurl may be installed. It requires no special privileges — just read access to the curl binary. Invoke as: bash check_cve_2026_92392.sh or bash check_cve_2026_92392.sh /path/to/curl if your curl is not in $PATH.
#!/usr/bin/env bash
# check_cve_2026_92392.sh — Detect CVE-2026-92392 exposure
# curl HTTP/2 upload use-after-free (affects 8.12.0 – 8.22.0)
# Usage: bash check_cve_2026_92392.sh [/path/to/curl]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
CURL_BIN="${1:-curl}"
if ! command -v "$CURL_BIN" &>/dev/null; then
echo "UNKNOWN — curl binary not found at '$CURL_BIN'"
exit 2
fi
VERSION_LINE=$("$CURL_BIN" --version 2>/dev/null | head -1)
VERSION=$(echo "$VERSION_LINE" | grep -oP '\d+\.\d+\.\d+' | head -1)
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not parse curl version from: $VERSION_LINE"
exit 2
fi
# Check HTTP/2 support — if absent, not vulnerable regardless of version
FEATURES=$("$CURL_BIN" --version 2>/dev/null)
if ! echo "$FEATURES" | grep -qi 'HTTP2\|nghttp2'; then
echo "PATCHED — curl $VERSION does not have HTTP/2 support compiled in (not affected)"
exit 0
fi
# Version comparison helper
ver_to_int() {
local IFS='.'
read -r major minor patch <<< "$1"
printf '%d%03d%03d' "${major}" "${minor}" "${patch}"
}
V=$(ver_to_int "$VERSION")
# Affected ranges:
# 8.12.0 – 8.14.2
# 8.15.0 – 8.16.1
# 8.17.0 – 8.20.1
# 8.21.0 – 8.22.0
# Fixed: 8.14.3, 8.16.2, 8.20.2, 8.23.0+
VULN=false
if [[ $V -ge $(ver_to_int 8.12.0) && $V -le $(ver_to_int 8.14.2) ]]; then
VULN=true
elif [[ $V -ge $(ver_to_int 8.15.0) && $V -le $(ver_to_int 8.16.1) ]]; then
VULN=true
elif [[ $V -ge $(ver_to_int 8.17.0) && $V -le $(ver_to_int 8.20.1) ]]; then
VULN=true
elif [[ $V -ge $(ver_to_int 8.21.0) && $V -le $(ver_to_int 8.22.0) ]]; then
VULN=true
fi
if $VULN; then
echo "VULNERABLE — curl $VERSION with HTTP/2 support is in the affected range (CVE-2026-92392)"
exit 1
else
echo "PATCHED — curl $VERSION is not in the affected range"
exit 0
fi