Last verified: October 14, 2026. This guide covers curl 8.23.0, released today.
The curl security update you should be installing right now is curl 8.23.0, released October 14, 2026. It fixes 24 security vulnerabilities, including one the curl project itself rates HIGH — its first High-severity flaw since the SOCKS5 heap overflow of October 2023. That flaw, tracked as CVE-2026-92392, lets a malicious server turn a routine HTTP/2 upload into remote code execution on the client. The kicker is not the bug itself. It is where curl lives: in your system package manager, your Docker base images, your CI runners, your PHP and Python stacks, and dozens of tools you have never audited. Patching one copy is not patching curl. This guide shows you how to find every copy and update it.
What this curl security update fixes
The headline vulnerability is CVE-2026-92392, an HTTP/2 upload use-after-free affecting curl and libcurl 8.12.0 through 8.22.0 — every release since February 2025. The remaining 23 advisories in this release are all rated low, and they cluster around familiar plumbing: credentials and connection reuse escaping their intended scope (Authorization Negotiate headers sent to foreign origins, host credentials leaking to an HTTP proxy, client certificates attached to the wrong reused connection), cookie-jar and HSTS edge cases, and protocol corner cases in FTP wildcards, TFTP, IMAP APPEND, and WebSockets under GnuTLS.
A few facts worth knowing before you act:
- The trigger is server-side. The victim uploads a file of roughly 135KB over HTTP/2 to an attacker-controlled server whose certificate passes the client’s checks. libcurl negotiates HTTP/2 automatically for HTTPS URLs when the server offers it and the build has HTTP/2 enabled.
- It was AI-found. The flaw was reported by Luna Tong at Anthropic using their new OSS Scanner. It landed on September 15 and was confirmed within hours — at which point the project shortened its release cycle from eight weeks to six to ship the fix sooner.
- Exploitation is hard, not impossible. curl says real-world code execution needs exact knowledge of the victim’s curl build, memory layout, distro, libc, and TLS bundle — and that the most likely outcome of an attempted attack is a crash. That is why it is High, not Critical. There are no reports of it being exploited in the wild.
- This release also closes an audit. 8.23.0 was curl’s fifth external official audit, and a large share of the 24 CVEs came out of an AISLE audit. Rock-solid curl builds were released in sync, and distros were alerted ahead of time via the distros@openwall list — so fixed packages are flowing through the normal channels right now.
How CVE-2026-92392 actually works (the plain-English version)
Understanding the mechanism matters because it explains why the fix is a version bump, not a config tweak. curl’s maintainer Daniel Stenberg published a detailed post-release write-up; here is the shape of it.
libcurl represents every transfer as an easy handle, and groups concurrent transfers under a multi handle. HTTP/2 is multiplexed, so curl is constantly juggling multiple logical transfers over one physical connection, each with a different lifetime.
The attack sequence goes like this: the client starts an upload over HTTP/2. The malicious server pauses the send by zeroing the HTTP/2 window, returns a piece of the response, then resets the stream — which kills the transfer but not the connection. curl correctly tears down the transfer, but accidentally keeps a lingering pointer to the freed easy handle. When the multi handle is cleaned up, libcurl misunderstands the situation, allocates download memory, finds the dangling pointer, and writes server-controlled data into the exact memory the easy handle used to occupy.
Two things then happen. First, the confused request leaks internal client details — including local addresses — which the server needs to defeat ASLR and build a working payload. Second, when libcurl processes the downloaded data through the dangling handle, it follows a function pointer the server’s data has just overwritten — and runs the attacker’s code.
The long-term fix, per the curl project, is to reduce the number of raw internal pointers to handles and instead store transfer identifiers that get looked up on demand — marginally slower, far more foolproof against this class of mistake — plus more tests and more static analysis. Multiplexed protocols make dangling pointers fatal; that is the lesson here, and it is why Stenberg says they keep iterating on the C codebase rather than attempting a rewrite.
Are you affected? Check every curl on your box
Start with the obvious one, then go looking for the ones you forgot about.
1. The system curl
curl --version
You want 8.23.0 or newer — but read the next section first, because distro packages complicate this. Check whether your build even has the vulnerable protocol enabled:
curl -V | grep -i http2
If HTTP2 appears in the Features line, HTTP/2 negotiation is on, which is the attack path. Now find the library behind the binary:
ldconfig -p | grep libcurl
Multiple libcurl versions on one box is common and entirely normal — and it is exactly the problem.
2. The language runtimes
PHP — curl is compiled into the interpreter or loaded as an extension. This will not track your system curl version:
php -r 'echo curl_version()["version"], PHP_EOL;'
# or check the loaded extension in phpinfo() output
Python — if you use pycurl rather than the pure-Python requests/httpx stack:
python -c "import pycurl; print(pycurl.version)"
Node.js — the popular node-libcurl binding ships its own bundled libcurl. Check the version bundled in your node_modules, not your system curl. Same story for Ruby’s curb gem and Rust’s curl crate: the version that matters is the one your dependency tree pulls in, which curl --version knows nothing about.
Where curl hides in your stack
This is the part most update guides skip. curl is infrastructure plumbing — it is everywhere precisely because nobody thinks about it. Walk this list:
- System package manager —
curlandlibcurl4on Debian/Ubuntu,curl/libcurlon RHEL/Fedora,curlon Alpine, Homebrew’s curl on macOS. Every VPS, every jump host, every workstation. - Docker base images —
debian:bookworm-slim,ubuntu:24.04,alpine, and every language image built on top of them ship distro curl. A pinned old image is a pinned old curl. Your multi-stage builds inherit it too. - CI/CD runners — GitHub Actions, GitLab runners, and self-hosted agents use curl in setup scripts and composite actions. Check the runner image, not just your app image.
- PHP builds — the
curlextension links its own libcurl. On Ubuntu this usually follows the system library; on compiled-from-source PHP setups and Docker images built withdocker-php-ext-install curl, it is whatever the build baked in. - Python, Node, Ruby, Rust apps — anything using pycurl, node-libcurl, curb, or the curl crate carries a bundled copy. Run
pip show pycurl/ check your lockfiles. - Statically linked binaries and vendored tools — some Go and Rust CLIs vendor a static libcurl.
stringson a suspicious binary or checking the vendor’s release notes is the only way to know. - Things that curl for you — package managers, Terraform providers, deployment scripts, monitoring agents, and cron jobs that
curla healthcheck endpoint all exercise the vulnerable code path when they upload over HTTPS.
How to update curl everywhere it hides
Package managers (the easy ones)
Update the package, then verify. Note that distros backport security fixes — your Ubuntu LTS box will not show 8.23.0, it will show a patched 8.x build from your distro’s security repository, and that is correct.
# Debian / Ubuntu
sudo apt update && sudo apt upgrade curl libcurl4
curl --version
# Check what's waiting before you commit
apt list --upgradable 2>/dev/null | grep curl
# RHEL / Fedora / Rocky / AlmaLinux
sudo dnf update curl libcurl
# Alpine
apk upgrade curl
# macOS
brew upgrade curl
# Windows
winget upgrade curl
On long-running servers, remember that running processes keep using the old library until they restart. Any daemon or cron worker that holds libcurl open — PHP-FPM workers, queue workers, monitoring agents — needs a restart after the package upgrade for the fix to take effect.
Docker images
You cannot apt upgrade your way out of a baked image. Rebuild every image whose base layer contains curl, with no cache, and re-push:
docker build --no-cache -t myapp:latest .
docker push myapp:latest
If you pin digest or old tags in production, the rebuild does not help until you deploy the new tag. And if your CI builds the image, fix it there first — otherwise your next deploy quietly reintroduces the vulnerable copy.
Language runtimes and vendored copies
- PHP — update the system libcurl, then restart PHP-FPM/Apache. For
docker-php-ext-install curlimages, rebuild the image. - Python pycurl —
pip install --upgrade pycurlafter updating system libcurl, since the wheel links against it; better still, rebuild the container. - Node / Ruby / Rust bindings — bump the binding version in your lockfile to one that vendors libcurl ≥ 8.23.0 (or a backported build), then
npm install/bundle install/cargo updateand redeploy. - Static/vendor binaries — watch the vendor’s release notes for a rebuild announcement. There is no command that fixes these; you replace the binary.
Building from source (the escape hatch)
If your distro has not shipped a fixed package yet and you cannot wait, build upstream 8.23.0 from the tarball on curl.se/download.html:
tar xzf curl-8.23.0.tar.gz
cd curl-8.23.0
./configure --with-openssl
make
sudo make install
This replaces the system curl, which package managers dislike. Prefer the distro package when it exists — source builds are for the gap between “vulnerability public” and “distro package ready”, which right now is measured in days.
Can’t apply the curl security update yet? Two stopgap mitigations
If a full rollout will take days — pinned production images, change freezes, third-party appliances — you can defuse the specific attack path without upgrading. The vulnerability only triggers over HTTP/2, so forcing HTTP/1.1 removes the trigger:
# curl CLI: pin a single invocation to HTTP/1.1
curl --http1.1 https://example.com/upload
For libcurl-based applications, the equivalent is setting the HTTP version option on the handle:
/* libcurl C API */
curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
Scope this narrowly: pin HTTP/1.1 only on the uploads and integrations that touch untrusted servers, and treat it as a bridge to the real patch, not a substitute. It costs you HTTP/2 multiplexing performance, and it does nothing for the other 23 advisories.
Why this curl security update matters beyond the patch
Three things make 8.23.0 worth paying attention to even after you have upgraded.
First, AI is now finding the bugs. CVE-2026-92392 came out of Anthropic’s OSS Scanner — an AI-assisted analysis pipeline. The curl team is frank about the double edge: AI-assisted tooling helped surface the flaw, but human review and validation remained essential to handling it. Expect more of these, not fewer.
Second, audits work, but they arrive in waves. A large portion of this release’s 24 CVEs traces back to an external AISLE audit — curl’s fifth official external audit. Independent audits keep finding real issues in one of the most scrutinised codebases on the planet. If curl, which is continuously probed by “countless humans and AIs”, still ships a High every few years, your dependencies deserve the same scepticism.
Third, the memory-safety debate got an answer, of a sort. Stenberg is explicit: libcurl stays in C. A rewrite in Rust would be, in his words, a monstrous undertaking — and the existing team are not Rust experts, while libcurl runs on more platforms than Rust targets. The strategy is instead to make the C code structurally safer: fewer raw handle pointers, more looked-up identifiers, more tests, more analysers. You can disagree with the choice, but it is an honest engineering position, and the transfer-identifier fix in this release is that philosophy in action.
The 60-second checklist
curl --version— is it 8.23.0+, or a distro-patched build released today?curl -V | grep -i http2— is the HTTP/2 attack path even compiled in?- Check
phpinfo()/ pycurl / node-libcurl / lockfiles for bundled copies. - Rebuild and redeploy Docker images — no cache, new tag, actually deployed.
- Restart long-running services (PHP-FPM, queue workers, agents) so they load the new libcurl.
- Stuck? Pin
--http1.1on untrusted uploads as a stopgap, then schedule the real patch.
curl is the kind of dependency you update and forget — until the day the update matters. Today is that day. Go find every copy.
Further Reading & References
- Official advisory: CVE-2026-92392 (curl.se) — the canonical advisory for the HTTP/2 upload use-after-free.
- Daniel Stenberg’s post-release write-up (daniel.haxx.se) — how the bug works, the trigger conditions, the fix strategy, and the memory-safety position, from curl’s lead maintainer.
- freenode: curl 8.23.0 fixes 24 vulnerabilities, one high-severity — full rundown of the low-severity cluster (credentials, cookies, HSTS, protocol edge cases).
- The Hacker News: curl patches HTTP/2 upload flaw after PoC demonstrates client-side code execution — the disclosure timeline, affected versions, and why curl expects real attacks to end in crashes.
- CSO Pakistan: curl releases security update for HTTP/2 upload memory vulnerability — mitigation notes (force HTTP/1.1 via
--http1.1/CURLOPT_HTTP_VERSION) and the note that older maintained branches also received fixes. - LavX News: curl to ship security fixes in accelerated release — the pre-release context: the shortened cycle and curl’s four-level severity scale (no CVSS).
- OpenSSH 10.6 Upgrade Checklist: What Breaks and What to Do About It — our earlier security-release checklist for your VPS, same playbook.
- How to Set Up Automated VPS Backups — patch days are a good reminder: back up before you upgrade anything on production.



