Skip to content

CVE-2026-92392: libcurl HTTP/2 Upload RCE Explained

CVE-2026-92392: libcurl HTTP/2 Upload RCE Explained
CVE-2026-92392 libcurl HTTP/2 upload use-after-free vulnerability header: severity HIGH, CWE-416, affects curl 8.12.0 to 8.22.0, fixed in 8.23.0
CVE-2026-92392 overview. Conceptual graphic. Source: Generated by UnpanicTech.

A use-after-free in libcurl's HTTP/2 upload handling lets a malicious server walk a dangling pointer into code execution on the client. Here's what the bug actually is, who's exposed, and exactly how to audit and fix it on Linux and in Docker.

On October 14, 2026, the curl project released version 8.23.0, fixing at least 24 vulnerabilities. One of them — CVE-2026-92392, titled HTTP/2 upload Use-After-Free — warranted shortening the release cycle by two weeks. Curl project lead Daniel Stenberg called it "one of the worst vulnerabilities in curl in a long time." [1]

The bug was reported by Luna Tong at Anthropic. It's a use-after-free in a C library that runs on essentially every Linux system, most Docker images, macOS, and hundreds of embedded applications — a reminder that the C memory model keeps handing attackers opportunities decades after the problem was first understood. [2]

What Actually Goes Wrong

CVE-2026-92392 lives in the way libcurl handles a specific HTTP/2 server response during an upload. To understand it, a small amount of background helps.

HTTP/2 is a multiplexed protocol — multiple logical transfers share one TCP connection, tracked by the library as separate streams. libcurl manages each transfer through an easy handle (struct Curl_easy), and groups them inside a multi handle. When you upload a file over HTTPS, libcurl negotiates HTTP/2 by default if the server supports it (via ALPN). Upload body data gets streamed as HTTP/2 DATA frames. [1]

The problem starts when a server zeroes its HTTP/2 flow-control window mid-upload, causing libcurl to park unsent body bytes in the easy handle and wait. Then the server sends an RST_STREAM frame with reason REFUSED_STREAM or HTTP_1_1_REQUIRED, which normally signals "try this again." libcurl marks the transfer for retry and calls multi_done() to clean up. [2]

The bug is in multi_done()'s cleanup order. Curl_conn_ev_data_done() runs first and calls req_flush(), which tries to push the parked upload bytes before declaring the connection event done. Flushing on a reset stream causes h2_submit() to call nghttp2_submit_request() again — creating a "phantom stream" in nghttp2 whose stream_user_data still points at the easy handle being torn down. Then the rest of the cleanup finishes, and the easy handle is freed. nghttp2 is now holding a stream whose user data points to freed heap. [3]

The next frame event on that phantom stream — for example, during connection pool teardown — reads through the dangling pointer. The freed memory, if an attacker can control what gets allocated there, becomes an attack surface.

CVE-2026-92392 Use-After-Free Sequence Four-step conceptual flow: HTTP/2 upload begins, server sends RST_STREAM, multi_done creates phantom stream pointing to easy handle, easy handle is freed then dereferenced via phantom stream 1 2 3 4 HTTP/2 Upload curl -Z -T file Easy handle E_A upload started Server RST_STREAM REFUSED_STREAM or HTTP_1_1_REQUIRED triggers multi_done() Phantom Stream req_flush() re-submits nghttp2 stream S' user_data → E_A UAF / RCE Risk E_A freed, S' still points to freed heap dereference at teardown CVE-2026-92392 — Use-After-Free sequence (conceptual)
Conceptual four-step sequence showing how CVE-2026-92392 creates a dangling pointer to a freed easy handle. Source: Generated by UnpanicTech.

From Use-After-Free to Remote Code Execution

A UAF by itself means an attacker might crash the process. CVE-2026-92392 goes further: a published proof-of-concept demonstrates a path to client-side remote code execution, though the conditions required are demanding. [3]

The attack requires the victim to be running a parallel upload — two simultaneous HTTPS requests, both to attacker-controlled servers with valid TLS certificates. No -k (insecure) flag, no certificate bypass: the attacker needs a legitimate cert. That alone eliminates a large fraction of theoretical victims.

Beyond that, Daniel Stenberg's writeup lists several conditions that must align simultaneously [1]:

  • The upload size must be close to ~135 KB — the attacker calibrates flow-control windows around this.
  • The attacker must know the exact curl version, architecture, and object layout to compute correct offsets.
  • Heap grooming — placing attacker-controlled bytes in E_A's freed slot — depends on a precise sequence of TLS 1.3 NewSessionTicket allocations and HPACK header operations, both of which vary between libc versions and Linux distributions.
  • The exploit leaks an internal address first (to defeat ASLR), then uses a second pass to overwrite a function pointer in the freed memory — calling system(cmd). The shell command payload fits in roughly 23 bytes.
  • Parallel mode (-Z) is needed for full RCE. A single-URL upload most likely causes a crash, not code execution.

JFrog's security researchers investigated whether less restrictive conditions could still produce code execution. Their conclusion: they could not achieve RCE outside the specific setup in the report. They characterized exploitation as a "once-in-a-blue-moon scenario" under current conditions. [3] The curl team graded this HIGH rather than CRITICAL for exactly this reason.

Still: a published PoC exists. The vulnerability is real, the mechanism is documented, and the crash path is easy to trigger even when RCE isn't. That's enough to warrant prompt patching, particularly anywhere curl or libcurl is used to upload data to third-party or less-trusted HTTPS endpoints.

As of this writing, CVE-2026-92392 is not listed in CISA's Known Exploited Vulnerabilities catalog, and no active exploitation in the wild has been reported by credible sources. [4]

Who's Affected — and Who Isn't

CVE-2026-92392 was introduced in curl 8.12.0 by a specific commit (cd9107e0) and is present through 8.22.0. Versions before 8.12.0 are not affected.

curl Branch Vulnerable Range Fixed In Notes
curl ≥ 8.23.0 Not affected 8.23.0 Upstream fix
8.21.x – 8.22.0 Vulnerable 8.23.0 Must upgrade
8.20.x Vulnerable up to 8.20.1 8.20.2 Backport available
8.16.x Vulnerable up to 8.16.1 8.16.2 Backport available
8.14.x Vulnerable up to 8.14.2 8.14.3 Backport available
curl < 8.12.0 Not affected N/A Vulnerable code not present
Debian bookworm Not affected N/A Ships curl 7.88.x — below introduced version
Debian trixie Vulnerable (8.14.1-2+deb13u5) 8.14.1-2+deb13u7 (DSA-6558-1) Security update issued
Ubuntu 26.04 LTS Patched via USN-8927-1 8.18.0-1ubuntu2.8 Apply updates

Sources: curl security advisory [2], Debian security tracker [5], Ubuntu USN-8927-1.

The bug also affects the curl command-line tool directly, not just libcurl as a library. Any application that embeds libcurl — Python's urllib internals, PHP, Ruby, Go tooling, or hundreds of programs that never surface curl in their documentation — may also carry the vulnerability if they were compiled against an affected version. [3]

How to Audit Your Systems

The first question is simple: what version of libcurl is actually running on your machines and inside your containers?

Check the Installed Version — Linux

On a standard Linux system:

curl --version
# Look for: curl 8.x.x — anything from 8.12.0 to 8.22.0 needs attention

# Check the libcurl shared library directly
dpkg -l libcurl4 libcurl3   # Debian/Ubuntu
rpm -q libcurl               # RHEL/CentOS/Fedora
ldconfig -p | grep libcurl   # Generic — shows linked library path

The output of curl --version shows both the curl tool version and the libcurl version it was built against. They're normally the same, but in some packaging scenarios they can differ — worth checking both.

Find All libcurl Binaries on a Host

Systems with multiple installed copies of libcurl (from different packages or build environments) may show a clean package manager result while still running a vulnerable binary elsewhere:

find /usr /opt /usr/local -name "libcurl.so*" 2>/dev/null
# Then check each one:
strings /path/to/libcurl.so | grep "libcurl/"

Audit Docker Images

Docker base images — Alpine, Ubuntu, Debian-slim — all bundle curl. If your Dockerfile pins a base image tag and hasn't been rebuilt since before October 14, the container may be running a vulnerable version. The right way to check without pulling and running the image:

# Check what curl version is inside a running container
docker exec <container_id> curl --version

# Check inside an image without running it
docker run --rm <image_name> curl --version

# For curlimages/curl specifically — update to latest or pin to 8.23.0+
docker pull curlimages/curl:8.23.0

This is the kind of transitive exposure that security teams often miss. For a deeper look at how dependency scanning fits into the broader picture, the vulnerability management process guide covers how to structure this systematically across your environment.

Assess Your Actual Exposure

Running libcurl 8.12.0–8.22.0 doesn't automatically mean you're on the RCE path. The critical question is whether your application:

  1. Makes HTTPS uploads (not just downloads) using libcurl
  2. Uses parallel transfer mode (-Z flag or multi-handle with concurrent connections)
  3. Uploads to third-party or potentially attacker-influenced endpoints

Applications that only download content over HTTPS are not exploitable via this path. The vulnerability lives specifically in the HTTP/2 upload cleanup logic. That said, even single-URL uploads to attacker-controlled servers can cause a crash — which is itself a denial of service vector. Patch regardless.

For services that scan pipeline artifacts automatically, JFrog's contextual analysis for CVE-2026-92392 correlates three conditions: nghttp2 support compiled in, a call to curl_easy_setopt, and an upload flag in use. If only the first condition is present, it returns "Undetermined" rather than a false positive. [3]

CVE-2026-92392 Mitigation Checklist Four action cards: 1. Upgrade curl to 8.23.0 on all hosts. 2. Rebuild Docker base images. 3. Workaround: force --http1.1. 4. Scan for embedded libcurl. ✅ 🐳 🛡️ 🔍 Upgrade Rebuild Images Workaround Scan Embedded curl/libcurl to 8.23.0+ on all hosts and CI/CD runners Pull updated base images (Alpine, Ubuntu, Debian) and rebuild Force HTTP/1.1 --http1.1 for uploads if immediate upgrade impossible Find libcurl.so* find /usr /opt Check bundled application copies
CVE-2026-92392 remediation priorities at a glance. Conceptual graphic. Source: Generated by UnpanicTech.

Mitigating CVE-2026-92392

The Right Fix: Upgrade to curl 8.23.0

The only complete fix is upgrading to curl 8.23.0, which addresses the bug at two levels. The primary fix (8b0744af) reorders cleanup in multi_done(): Curl_req_done() now runs before the connection event handler, so no request-body flush can happen while the connection is being torn down — the phantom stream is never created. A second defense-in-depth commit (d9295430) removes the raw Curl_easy* pointer from nghttp2's stream user data entirely, replacing it with a lookup by stream ID through the multi handle's table. A stale stream ID resolves to nothing instead of a dangling pointer. [3]

For your distro packages, check and apply security updates — most major distributions have issued backported fixes as well. Ubuntu addresses CVE-2026-92392 in USN-8927-1; Debian trixie in DSA-6558-1. [5]

# Debian/Ubuntu
sudo apt update && sudo apt upgrade curl libcurl4

# RHEL/Rocky/AlmaLinux
sudo dnf update curl libcurl

# Alpine
apk update && apk upgrade curl

If You Can't Upgrade Immediately

The curl advisory offers a clear workaround: force HTTP/1.1 for uploads. [2] HTTP/2 is where the bug lives; disabling it eliminates the vulnerable code path entirely.

# Force HTTP/1.1 for a specific upload
curl --http1.1 -T /path/to/file https://example.com/upload

# Or set via libcurl option in application code:
# CURLOPT_HTTP_VERSION = CURL_HTTP_VERSION_1_1

This is a workaround, not a fix — it means trading HTTP/2 multiplexing for safety until the upgrade happens. If your application relies on parallel uploads for performance, plan for the version upgrade rather than running on HTTP/1.1 indefinitely.

Integrating libcurl version checks into your container scanning pipeline is worth doing regardless of this specific CVE. For a broader framework on how to handle this kind of scanning and triage continuously, the CVSS 4.0 triage guide explains how to make prioritization decisions across a queue of vulnerabilities like the 24 addressed in this curl release.

Docker-Specific Steps

Update your Dockerfiles to pull a base image version that carries the patch, and rebuild. If you use curlimages/curl, pin to 8.23.0 or use latest and pull fresh. For images you don't control directly — third-party CI runners, for example — run a version check as part of the pipeline:

# Add to your Dockerfile after base image pull:
RUN curl --version | grep -E "curl (8\.(1[2-9]|2[0-2])\.|8\.(12|13|14|15|16|17|18|19|20|21|22))" \
    && echo "WARNING: Vulnerable curl version detected" || true

# Better: just upgrade in the Dockerfile
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

Kubernetes environments using hardened container configurations should treat base image updates as a routine part of their patch cadence — this CVE is a good argument for automating base image upgrades via Dependabot or Renovate rather than relying on manual pulls.

Why This Bug Matters Beyond the Headline

The honest headline for CVE-2026-92392 is: a real use-after-free in one of the most widely distributed C libraries on the planet, with a published RCE proof-of-concept, but an exploit scenario that requires fairly specific conditions to succeed. A crash is easy; controlled code execution is hard. JFrog couldn't reproduce it outside the prescribed setup. That matters for prioritization.

What also matters is the bug's origin story. It was introduced by a commit in curl 8.12.0, reported to the curl team on September 15, 2026, and fixed in a release that shipped just six weeks later — the team cut two weeks off their normal release cycle because of it. The reporter is credited as "Luna with Claude, Anthropic," which the curl project and Daniel Stenberg have noted appears to involve an Anthropic OSS Scanner using AI models. [2] [1]

Stenberg also addressed the C language question directly in his post — not for the first time in curl's history. He acknowledged that a memory-safe language would likely have prevented this class of mistake, while also explaining why a full rewrite of curl isn't realistic: the library runs on hundreds of operating systems and dozens of CPU architectures, and the curl team doesn't have Rust experts on staff. That's a candid and honest position. It doesn't mean curl is reckless with security; it means the team is working within real constraints while iterating on mitigations like replacing raw pointer stores with ID-based lookups. [1]

For defenders, the practical takeaway is the same whether you think the risk is HIGH or somewhere between HIGH and "probably a crash": curl 8.12.0–8.22.0 is affected, 8.23.0 fixes it, and the workaround is two words (--http1.1). An incident response plan that includes scheduled library audits would have caught this during your normal patch window — this isn't a zero-day requiring emergency response, but it does warrant prompt attention given the RCE proof-of-concept's existence.

What Remains Uncertain

NVD had not completed CVSS scoring for CVE-2026-92392 at the time of publication. The curl project does not publish CVSS scores for its own vulnerabilities as a matter of policy, and third-party scores will vary. [2] The curl advisory designates severity HIGH and CWE-416 (Use After Free); those are the authoritative classifications available right now.

No active exploitation has been reported. The CISA KEV catalog did not list CVE-2026-92392 as of October 16, 2026. That status should be rechecked as the vulnerability ages, since published proof-of-concept code tends to lower the bar for opportunistic attempts over time.


Sources & References

  1. Daniel Stenberg — A UAF turned RCE (daniel.haxx.se, October 14, 2026)
  2. curl Project Security Advisory — CVE-2026-92392: HTTP/2 upload Use-After-Free (curl.se, October 14, 2026)
  3. JFrog Security Research — CVE-2026-92392: curl HTTP/2 upload RCE (research.jfrog.com, October 15, 2026)
  4. The Hacker Wire — CVE-2026-92392 threat intelligence (CISA KEV status: Not in catalog)
  5. Debian Security Tracker — CVE-2026-92392 package status
NK

Cybersecurity Researcher & Technical Editor

UnpanicTech publishes independent cybersecurity research and defensive security analysis. Naseem Khan serves as Technical Editor, with a focus on vulnerability analysis, defensive security, incident response, cloud security, and practical security engineering.