Safeguard
Open Source Security•

curl 8.23.0 Fixes 24 CVEs: The One to Prioritise and How to Find Every libcurl You Ship

curl 8.23.0, released on 14 October 2026, fixes 24 CVEs in one go. That's more than curl published in all of 2024 and 2025 combined, and it takes the project's 2026 total to 69.

Safeguard Security Team
Security Research
8 min read

curl 8.23.0, released on 14 October 2026, fixes 24 CVEs in one go. That's more than curl published in all of 2024 and 2025 combined, and it takes the project's 2026 total to 69. Twenty-three of the 24 are rated Low. One, CVE-2026-92392, is rated High: a use-after-free during HTTP/2 uploads that the curl team showed can be turned into remote code execution by a malicious server, under a long list of conditions.

The hard part isn't upgrading the curl command. It's finding all the other copies of libcurl, the ones bundled into applications, language runtimes and container images that never show up as "curl" in an inventory. This guide is for teams that ship or run software on Linux, macOS or Windows. It covers which of the 24 to prioritise, how to mitigate if you can't upgrade, and a script that finds embedded libcurl builds by the version string compiled into them.

The short answer

Upgrade to curl 8.23.0, or to the patched branch releases 8.14.3, 8.16.2 or 8.20.2. CVE-2026-92392 affects curl and libcurl 8.12.0 to 8.22.0 when they upload over HTTP/2. If you can't upgrade a component straight away, forcing HTTP/1.1 avoids the bug. Then look beyond the command-line tool: any application that bundles its own libcurl needs its vendor's update. The other 23 CVEs are Low severity and can follow your normal patch cycle.

CVE-2026-92392: the one to prioritise

curl's advisory describes the bug in one sentence: during an upload, curl mishandles an HTTP/2 RST_STREAM from the server with reason HTTP_1_1_REQUIRED or REFUSED_STREAM, which leads to a use-after-free of a struct Curl_easy object. The server sends the reset, so a malicious or compromised server, or anything that can sit in the middle of the connection, controls the trigger.

Affectedcurl and libcurl 8.12.0 to 8.22.0 (the library and the command-line tool)
Fixed8.23.0; also 8.14.3, 8.16.2 and 8.20.2 on those branches
SeverityHigh (curl project rating), CWE-416
Reported15 September 2026; fixed and published 14 October
Credit"Luna with Claude" and Anthropic as finders; Stefan Eissing developed the fix

The advisory is unusually precise about exploitation. The curl team says the use-after-free "has been proven to be possible to convert into a Remote Code Execution", but only if the malicious server knows, among other things, the exact curl version and structure layout, the architecture, the libc allocator build and heap layout, nghttp2 and TLS 1.3 behaviour including session ticket allocations, and a pinned CA bundle. It also needs two parallel transfers with the right timing, and the proven exploit is limited to a command of about 23 bytes. That's a high bar for a general attack, and a reachable one for a targeted attacker who knows exactly what you run.

The project lists three remedies: upgrade, apply the patch, or don't use HTTP/2. (For the last libcurl bug that drew this much attention, see our post on CVE-2023-38545.) For the third, the command-line tool takes --http1.1, and applications can set CURLOPT_HTTP_VERSION to CURL_HTTP_VERSION_1_1.

The other 23

The remaining CVEs are all rated Low. Many involve credentials or state leaking where they shouldn't: Negotiate credentials sent to redirected or foreign origins, host credentials leaked to an HTTP proxy, a password leaking into CURLINFO_EFFECTIVE_URL, cookie jar and HSTS cache handling mistakes, and connection reuse mixups. Most need specific options or protocols to matter. If your software uses Negotiate authentication, proxies, SFTP or SCP redirects, or wolfSSL, read the individual advisories on curl's vulnerability table. Otherwise they ride along with the upgrade.

Who found them is part of the story. Eighteen of the 24 are credited to Aisle Research, two (including the High one) to Anthropic and a researcher working with Claude, and four to other individuals. curl published 9 CVEs in 2025 and 69 so far in 2026, 53 of them rated Low. The project hasn't become less safe. Automated and AI-assisted review is finding far more small bugs, and curl is publishing each one.

Where libcurl hides

libcurl is one of the most widely embedded libraries there is, and curl's own advisory warns that it is "not always advertised as such". Common places it turns up:

  • Operating system packages. Your distribution's curl and libcurl packages, which many other packages link to. Git's HTTP transport and PHP's curl extension are two familiar examples.
  • Language packages that bundle their own copy. Binary wheels, gems and native modules often ship a private libcurl so they work without system libraries.
  • Desktop and server applications. Vendors frequently bundle libcurl inside the application, where your package manager can't see or update it.
  • Container images. Base images include it, and images built FROM an old base keep the old copy.

Distribution packages are the easy part, because they're tracked and often patched by backporting the fix without changing the upstream version number. The bundled copies are the problem, because only their vendor can update them.

Find embedded libcurl builds

libcurl compiles its version into the binary as a string of the form libcurl/8.22.0. This script walks directories, opens only executables and libraries (ELF, Mach-O and PE files), and reports every libcurl version string it finds, marking the versions in CVE-2026-92392's range. It reads files and runs nothing, and it uses only the standard library.

#!/usr/bin/env python3
"""Find curl and libcurl builds on disk by the version string compiled into them, including
copies bundled inside applications, Python wheels and container image layers you have unpacked.
Flags versions affected by CVE-2026-92392 (HTTP/2 upload use-after-free, fixed in 8.23.0).
Standard library only; it reads files and runs nothing.
Usage: python3 find_libcurl.py DIR [DIR ...]"""
import os, re, sys

VERSION = re.compile(rb"libcurl/(\d+)\.(\d+)\.(\d+)")
MAGIC = (b"\x7fELF", b"MZ", b"\xcf\xfa\xed\xfe", b"\xca\xfe\xba\xbe", b"\xce\xfa\xed\xfe")  # ELF, PE, Mach-O
FIXED_BRANCHES = {(8, 14): 3, (8, 16): 2, (8, 20): 2}  # backported fixes: 8.14.3, 8.16.2, 8.20.2

def affected(v):
    if v < (8, 12, 0) or v >= (8, 23, 0):
        return False
    patch = FIXED_BRANCHES.get(v[:2])
    return not (patch is not None and v[2] >= patch)

def scan_file(path):
    try:
        with open(path, "rb") as f:
            if not f.read(4).startswith(MAGIC):
                return set()
            f.seek(0)
            data = f.read(64 * 1024 * 1024)   # version strings sit in read-only data; 64 MB is plenty
    except OSError:
        return set()
    return {tuple(int(x) for x in m.groups()) for m in VERSION.finditer(data)}

hits = 0
for top in sys.argv[1:]:
    for dirpath, dirs, files in os.walk(top):
        for name in files:
            path = os.path.join(dirpath, name)
            if os.path.islink(path) or not os.path.isfile(path):
                continue
            for ver in sorted(scan_file(path)):
                hits += 1
                status = "AFFECTED" if affected(ver) else "ok      "
                print(f"{status} libcurl {'.'.join(map(str, ver)):<8} {path}")
print(f"\n{hits} libcurl build(s) found. CVE-2026-92392 affects 8.12.0 to 8.22.0; fixed in 8.23.0 "
      "(and 8.14.3, 8.16.2, 8.20.2). Distribution packages may carry the fix as a backport.")

We ran it over /Applications, /opt/homebrew and /usr/local on a Mac used for this blog. The system curl lives in macOS's shared library cache, not on disk, so it didn't appear. The scan took about 80 seconds and found one bundled copy, in a widely used desktop application:

$ python3 find_libcurl.py /opt/homebrew /Applications /usr/local
AFFECTED libcurl 8.17.0   /Applications/<application>.app/Contents/Frameworks/<framework>

1 libcurl build(s) found. CVE-2026-92392 affects 8.12.0 to 8.22.0; fixed in 8.23.0 (and 8.14.3, 8.16.2, 8.20.2). Distribution packages may carry the fix as a backport.

We've hidden the application's name. A version in the affected range doesn't mean that application is exploitable, because it only matters if the app uploads over HTTP/2 to a server an attacker controls or can intercept. But it's exactly the kind of copy no package manager will update for you. For container images, point the script at an unpacked image filesystem, for example the output of docker export or skopeo copy to a directory, and for Linux hosts, at /usr, /opt and your application directories.

Two limits: a version string can't tell you about backported fixes, so a distribution's libcurl/8.5.0 may already be patched, and a build with the string stripped won't show up. Treat the output as an inventory to check against vendor and distribution advisories, not a final verdict.

The fair view

Twenty-four CVEs in one release sounds alarming, and it isn't. Twenty-three are Low, most need unusual configurations, and the High one needs a malicious server plus a precise picture of your build before it becomes more than a crash. The curl team's habit of publishing every confirmed issue, with exploit conditions spelled out, is a large part of why the number is high.

What the release does show is the gap between fixing a library and fixing everything that contains it. Upgrading curl takes a minute. Finding the copy inside an application bundle, a Python wheel or a two-year-old base image takes an inventory most teams don't have. An SBOM for every image and application you ship is how you'd answer "where do we run libcurl 8.12 to 8.22?" in minutes rather than days.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.