כותרת Penligent

CVE-2026-10536: libcurl HTTP/2 Stream-Dependency UAF Explained

CVE-2026-10536 is a use-after-free vulnerability in libcurl’s handling of HTTP/2 stream-dependency state. The flaw can occur when an application establishes a dependency between libcurl easy handles through CURLOPT_STREAM_DEPENDS או CURLOPT_STREAM_DEPENDS_E, resets a related handle with curl_easy_reset(), and later destroys it with curl_easy_cleanup().

During the final cleanup operation, affected libcurl versions can attempt to access and modify an internal structure that was already freed during the earlier reset. The result is an invalid memory access that may be detected by AddressSanitizer or Valgrind, terminate a debug build at an assertion, or crash an ordinary application process. (תלתל)

The affected upstream range is curl 7.88.0 through 8.20.0, inclusive. The vulnerability was fixed in curl 8.21.0, released on June 24, 2026. Versions earlier than 7.88.0 are outside the vulnerable range because the affected implementation had not yet been introduced. The curl command-line tool is not affected; the issue concerns applications that directly use the relevant libcurl programming interfaces. (תלתל)

CVE-2026-10536 is especially notable because its public severity ratings differ dramatically. The curl project rates the flaw Low. Red Hat rates its product impact Moderate with a CVSS 3.1 score of 4.7. Meanwhile, the NVD page displays a CISA-ADP score of 9.8 Critical, while also stating that NVD has not yet provided its own assessment. (NVD)

This disagreement makes the vulnerability a useful case study in why defenders should not treat a CVSS score as a complete exploitability assessment. libcurl is widely embedded, but the vulnerable execution path is highly specific. Exposure depends not only on whether an affected libcurl version is present, but also on whether the consuming application uses two rarely used HTTP/2 dependency options and follows the relevant reset-and-cleanup lifecycle.

CVE-2026-10536 at a Glance

קטגוריהVerified information
סוג הפגיעותUse-after-free
Affected componentlibcurl
Relevant protocol featureHTTP/2 stream dependencies
Required optionsCURLOPT_STREAM_DEPENDS או CURLOPT_STREAM_DEPENDS_E
Required lifecycle callscurl_easy_reset() followed by curl_easy_cleanup()
גרסאות מושפעותcurl 7.88.0 through 8.20.0
Fixed versioncurl 8.21.0
curl CLI affectedלא
curl project severityנמוך
Red Hat severityModerate, CVSS 4.7
CISA-ADP score displayed by NVD9.8 Critical
Directly supported impactInvalid memory access, assertion failure or application crash
Preferred remediationUpgrade to 8.21.0 or a vendor-patched package
Temporary mitigationStop using HTTP/2 stream-dependency options

The curl advisory identifies the weakness as CWE-416, Use After Free. It says that the two relevant dependency options are extremely rarely used and that HTTP/2 dependencies are generally considered deprecated. The official recommendations are to upgrade to 8.21.0, apply the upstream patch to an existing branch, or avoid HTTP/2 stream dependencies. (תלתל)

What Is CVE-2026-10536?

A use-after-free occurs when software continues to access an object after the memory associated with that object has been released or otherwise invalidated.

In C, freeing memory does not automatically invalidate every pointer that previously referenced that allocation. A stale pointer can remain inside another object, a linked list, a callback structure, a cache or an ownership tree. When the program later follows that pointer, the resulting behavior is undefined.

The freed region might temporarily retain its previous contents, allowing the program to appear functional during testing. It might be reused for a different allocation, causing the program to read or modify unrelated state. A hardened allocator, assertion, AddressSanitizer or Valgrind may detect the access and terminate the process.

In more severe circumstances, a sufficiently controllable use-after-free can become a route to memory disclosure, data corruption or code execution. However, the existence of the weakness class does not by itself prove that every affected application is remotely exploitable or that arbitrary code execution has been demonstrated.

CVE-2026-10536 concerns an internal structure used to represent HTTP/2 stream-dependency relationships between libcurl easy handles. Applications could establish those relationships through:

CURLOPT_STREAM_DEPENDS
CURLOPT_STREAM_DEPENDS_E

After a dependency relationship had been created, a specific sequence involving curl_easy_reset() ו curl_easy_cleanup() could leave the final cleanup path operating on state that was no longer valid.

The official curl description states that libcurl attempts to access and modify an internal structure that had already been freed during the reset operation. This makes the vulnerability a lifecycle-management defect inside an optional HTTP/2 feature rather than a generic parser bug affecting every HTTP/2 request. (תלתל)

גרסאות מושפעות ותוקנות

The affected upstream range is:

curl and libcurl 7.88.0 through 8.20.0

The following versions are outside the affected upstream range:

Versions earlier than 7.88.0
Versions 8.21.0 and later

The vulnerable implementation was introduced in the curl 7.88.0 development history. The official fix was committed as bfbff7852f050232edd3e5ca5c6bf2021c340f5a, which removed the internal HTTP/2 stream-dependency tracking implementation. curl 8.21.0 was released on June 24, 2026, in coordination with publication of the security advisory. (תלתל)

Version comparison is not always sufficient for Linux distributions. Enterprise and long-term-support distributions frequently backport individual security patches without changing the visible upstream version to 8.21.0.

A package that appears older than 8.21.0 may therefore already include the fix. Conversely, a package whose upstream version falls within the affected range may remain vulnerable until its distribution publishes an updated package revision.

The Debian tracker illustrates why package-specific verification matters. As of the reviewed tracker data, Debian’s older bullseye package is not affected because it uses curl 7.74.0, while some bookworm and trixie package revisions remain listed as vulnerable. Debian’s unstable branch contains a fixed 8.21.0-based package. Debian also characterizes the issue as minor and explicitly notes the need for the rare stream-dependency plus reset-and-cleanup pattern. (Debian Security Tracker)

Security teams should answer two separate questions:

  1. Is the deployed code derived from an affected upstream version?
  2. Has the operating-system or software vendor backported the official fix?

A scanner that relies only on a simple version comparison may generate false positives against backported packages and false negatives against statically linked or privately bundled copies.

The curl Command-Line Tool Is Not Affected

One of the most important scope distinctions is that the תלתל command-line application is not affected by CVE-2026-10536.

The vulnerable options belong to libcurl’s application programming interface. They are intended for programs that create multiple easy handles and explicitly configure HTTP/2 stream relationships between those handles. The ordinary curl command-line tool does not use the vulnerable dependency lifecycle, and the curl project states directly that the CLI is unaffected. (תלתל)

This distinction matters because many asset scanners detect a תלתל executable and immediately mark the host vulnerable.

Finding the executable is useful for software inventory, but it does not demonstrate that any application reaches the vulnerable code path. A system can contain several independent curl or libcurl builds:

  • The system-provided curl executable
  • The system-provided shared libcurl library
  • A statically linked copy inside an application
  • A private shared library bundled by a vendor
  • A language binding linked to a different libcurl build
  • A copy inside a container image
  • An embedded copy inside firmware or a desktop application

Running:

curl --version

reports information about the curl command-line build. It does not necessarily identify the libcurl version loaded by another process.

The correct unit of analysis is therefore the consuming application and its runtime dependency, not simply the machine on which a curl executable exists.

Why HTTP/2 Had Stream Dependencies

HTTP/2 allows many request and response streams to share a single connection. Because those streams compete for bandwidth and processing resources, the original HTTP/2 design included a priority system based on stream weights and dependency relationships.

Conceptually, a client could express a hierarchy such as:

Connection root
├── Main document
│   ├── Critical stylesheet
│   └── Application script
└── Lower-priority background resource

The intended goal was to tell a server that some streams should receive resources before others.

In practice, the original priority scheme proved difficult to use consistently. RFC 9113 explains that clients expressed priorities in inconsistent ways, generic server-side support was complex, implementations were uneven and many server deployments ignored client priority signals.

RFC 9113 consequently deprecated the priority signaling defined in the earlier HTTP/2 specification. The RFC describes the original prioritization scheme as unsuccessful and deprecates PRIORITY frames and the related dependency signaling. (עורך RFC)

This protocol history influenced curl’s remediation strategy. Instead of repairing and continuing to maintain a complicated internal dependency tree for a rarely used and deprecated feature, the curl project removed stream-dependency tracking.

The upstream fix says that the HTTP/2 feature was deprecated, few servers implemented it and libcurl’s implementation was complicated by state management. The two related CURLOPT_* options were converted into no-ops and marked deprecated. (GitHub)

Understanding the Relevant libcurl Lifecycle

To understand CVE-2026-10536, it helps to separate four operations:

  1. Creating an easy handle
  2. Configuring its options
  3. Resetting the handle
  4. Destroying the handle

A normal libcurl easy-handle lifecycle may look like this:

#include <curl/curl.h>

int main(void)
{
    CURL *handle = curl_easy_init();

    if(handle) {
        curl_easy_setopt(handle,
                         CURLOPT_URL,
                         "https://example.com/");

        curl_easy_perform(handle);
        curl_easy_cleanup(handle);
    }

    return 0;
}

curl_easy_init() creates the easy handle. curl_easy_setopt() configures its behavior. curl_easy_perform() executes a transfer. curl_easy_cleanup() ends the handle and releases its associated resources.

Some applications reuse handles rather than creating a new one for every transfer. They may call:

curl_easy_reset(handle);

The official documentation says that curl_easy_reset() restores previously configured options to their default values. However, it does not remove all state associated with the handle. Live connections, session information, DNS cache entries, cookies, shares and alt-svc cache information can remain. (תלתל)

By comparison:

curl_easy_cleanup(handle);

permanently terminates the easy handle. The handle must not be used after cleanup.

The vulnerability lies at the boundary between resetting option-related state and subsequently performing final object destruction.

מה CURLOPT_STREAM_DEPENDS Did

CURLOPT_STREAM_DEPENDS allowed an application to tell libcurl that the HTTP/2 stream associated with one easy handle depended on another stream.

A simplified relationship might look like this:

Parent easy handle
        │
        └── Child easy handle

The exclusive variant, CURLOPT_STREAM_DEPENDS_E, represented an exclusive dependency relationship and could require more significant restructuring of the priority hierarchy.

Because dependencies connected multiple easy handles, libcurl needed internal state that tracked parent-child relationships. One handle could not be managed as a completely isolated object because another handle might contain a reference to it.

That creates difficult lifecycle questions:

  • What happens when the child is reset?
  • What happens when the parent is reset?
  • What happens when one handle is destroyed first?
  • Should children be detached or reparented?
  • Which object owns each dependency node?
  • Can cleanup run after part of the hierarchy has already been removed?
  • Does an error path follow the same ownership rules as a successful transfer?

The vulnerable implementation failed to preserve a valid internal state across one of these reset-and-cleanup sequences.

The CVE-2026-10536 Trigger Sequence

The official vulnerability description identifies three essential stages.

Stage One: Configure a stream dependency

The application uses one of the following options:

CURLOPT_STREAM_DEPENDS
CURLOPT_STREAM_DEPENDS_E

This is the most restrictive prerequisite. Most applications using libcurl never call either option. The curl advisory describes them as extremely rarely used. (תלתל)

Stage Two: Reset a related easy handle

The application subsequently calls:

curl_easy_reset(handle);

The reset operation returns configured options to their defaults and participates in invalidating or freeing the relevant dependency state.

Stage Three: Clean up the handle

At a later point, the application calls:

curl_easy_cleanup(handle);

During final cleanup, the vulnerable implementation may attempt to access or modify a dependency structure that was already freed during the reset.

The important point is that none of these API calls is inherently unsafe in ordinary use. The defect emerges from the interaction between a cross-handle dependency relationship and a particular object-lifecycle sequence. (תלתל)

A Simplified Failure Model

The internal failure can be represented conceptually as follows:

1. Two easy handles are created.

   parent
   child

2. A stream dependency is configured.

   parent ─────► child

3. libcurl stores cross-object dependency state.

   parent.children includes child
   child.parent refers to parent

4. One related handle is reset.

   Dependency-associated memory is released
   or invalidated during reset.

5. Final cleanup later executes.

   Cleanup logic still assumes that the
   dependency structure remains valid.

6. The stale structure is accessed.

   Result:
   - AddressSanitizer error
   - Valgrind invalid access
   - Debug assertion failure
   - Application crash
   - Undefined behavior

This model is deliberately simplified. The authoritative fact is that final cleanup accesses and modifies a structure already freed during reset. The model explains why cross-object state is difficult to manage: freeing state from one lifecycle path is unsafe when another path still assumes that the state exists. (תלתל)

Why Reset Functions Are Security-Sensitive

Reset APIs often appear less dangerous than destruction APIs. Developers may assume that a reset simply assigns default values to a group of fields.

In a complex native library, a reset may need to release dynamically allocated memory, detach callbacks, remove entries from queues, invalidate protocol state and reconcile relationships with other objects.

The difficult part is not clearing an object’s own fields. It is preserving every invariant shared with other objects.

Consider a generic parent-child implementation:

struct node {
    struct node *parent;
    struct node **children;
};

If the child is reset and its local state is freed, the parent’s children collection must also be updated. If the parent is reset, each child’s parent pointer must be cleared or redirected.

A partial cleanup can leave either side holding stale data.

The same principle applies to:

  • Event-loop callbacks
  • Connection pools
  • Promise or future chains
  • GUI object trees
  • Reference-counted objects
  • Cache entries
  • Asynchronous request queues
  • Plugin ownership structures
  • Agent task hierarchies

CVE-2026-10536 is therefore not only an HTTP/2 issue. It is an example of a general secure-engineering problem: reset paths must be treated as complete lifecycle transitions, not as collections of independent field assignments.

Why a Malicious HTTP/2 Server Is Not Enough by Itself

Some summaries may describe CVE-2026-10536 as a network vulnerability because libcurl communicates with remote HTTP/2 servers. That wording can create the impression that any malicious server can send a crafted HTTP/2 response and compromise an ordinary libcurl client.

The verified trigger conditions do not support such a broad conclusion.

The consuming application must first use CURLOPT_STREAM_DEPENDS או CURLOPT_STREAM_DEPENDS_E. It must subsequently call curl_easy_reset() and later curl_easy_cleanup() on the relevant handle.

A remote HTTP/2 response does not automatically cause an arbitrary application to begin using those programming interfaces.

This does not mean remote influence is impossible in every product. An application might allow untrusted input to affect:

  • Which transfers are created
  • Which streams are related
  • Whether a request is cancelled
  • Whether an easy handle is reset
  • Whether a worker is destroyed
  • The order in which related handles are cleaned up
  • Retry and timeout behavior
  • Connection-pool reuse

A multi-tenant service might expose an API that indirectly controls request orchestration. A browser-like engine might create dependent resources based on remotely supplied content. A downloader might change its lifecycle based on server resets or transfer errors.

In those products, remote reachability is possible in principle, but it must be demonstrated at the application level.

A meaningful analysis should therefore follow this sequence:

Does the application use an affected libcurl version?
        ↓
Does it use CURLOPT_STREAM_DEPENDS or _E?
        ↓
Can a related handle be reset afterward?
        ↓
Does final cleanup access stale dependency state?
        ↓
Can an untrusted party influence that lifecycle?
        ↓
Does the resulting failure cross a security boundary?

Without the first four conditions, the published vulnerable path is not reached. Without attacker influence, the issue may remain a locally triggered reliability defect rather than a remotely exploitable application vulnerability.

What Is the Real Security Impact?

The most directly supported consequence is an invalid memory access that can terminate the consuming application.

The curl advisory states that Valgrind and AddressSanitizer detect the problem and that a debug build aborts on an assertion. Red Hat describes the practical impact in its products as application crashes and denial of service. (תלתל)

Because the issue is a use-after-free in C, it should not be dismissed as an ordinary logic error. Undefined behavior can produce different results depending on the allocator, compiler, optimization level, heap layout, timing and surrounding allocations.

However, defenders should distinguish between a possible weakness-class consequence and a demonstrated exploit outcome.

The verified public information establishes that:

  • A use-after-free exists.
  • The invalid access is detectable with memory-safety tooling.
  • Debug builds can terminate at an assertion.
  • Applications may crash.
  • The curl command-line tool is unaffected.
  • The vulnerable APIs are rarely used.
  • The feature is deprecated.
  • Application-specific reachability is required.

The public vendor material reviewed here does not establish a universal remote-code-execution chain against all applications using affected libcurl versions.

Red Hat’s weakness discussion notes that expired pointers can theoretically contribute to memory reads, crashes or unauthorized code execution depending on how they are used. That is a generic explanation of the weakness class, not proof that CVE-2026-10536 has been weaponized into reliable code execution in a real product. (Red Hat Customer Portal)

Why Do the Severity Ratings Conflict?

CVE-2026-10536 Reachability and Remediation Decision Path

CVE-2026-10536 has an unusually large severity gap.

curl project: Low

The curl project rates the vulnerability Low.

That classification reflects the extremely uncommon API requirements, the deprecated nature of the feature, the lack of impact on the curl CLI and the narrow lifecycle needed to trigger the bug. (תלתל)

Red Hat: Moderate, CVSS 4.7

Red Hat assigns the following CVSS 3.1 vector:

CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H

This produces a base score of 4.7.

Red Hat’s analysis treats application crash and denial of service as the realistic impact for its product context. It also emphasizes that the vulnerability requires a precise application programming pattern, limiting exploitability in typical Red Hat deployments. (Red Hat Customer Portal)

CISA-ADP: 9.8 Critical

The NVD page displays the following CISA-ADP vector:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

This produces a base score of 9.8 Critical.

However, the same page clearly states that NVD has not yet provided its own assessment. It is therefore inaccurate to call 9.8 an independently calculated “NVD score.” It is a score contributed by CISA-ADP and displayed by NVD. (NVD)

The NVD change record also identifies the associated HackerOne report as an exploit-related reference and records a CISA SSVC value of exploitation: poc. That indicates the existence of proof-of-concept reproduction information, but it does not prove reliable remote exploitation of every product embedding libcurl. (NVD)

Debian: Minor

Debian describes the issue as minor and notes that it requires the rare combination of CURLOPT_STREAM_DEPENDS and the reset-and-cleanup sequence. (Debian Security Tracker)

How defenders should interpret the disagreement

These ratings reflect different assumptions about:

  • Whether the flaw is remotely reachable
  • Whether an attacker controls the application lifecycle
  • How much complexity is required
  • Whether privileges are necessary
  • Whether confidentiality and integrity impact are realistic
  • Whether the primary consequence is only a crash
  • How the affected code is compiled and deployed
  • Whether the application even uses the vulnerable feature

CVSS is a model, not an exploit demonstration.

A high score can be useful as a signal to investigate, but it should not replace code reachability analysis. Likewise, a Low upstream rating should not justify ignoring a confirmed memory-safety defect in a privileged or critical service.

A Reachability-Based Risk Matrix

Deployment conditionPractical interpretationSuggested priority
libcurl older than 7.88.0Vulnerable implementation is absentInformational
libcurl 8.21.0 or laterUpstream fix is presentVerify deployment
Vendor package contains backportVisible version may look old, but fix may be presentVerify vendor revision
Affected version without HTTP/2 supportRelevant feature path is unlikely to existנמוך
HTTP/2 enabled, dependency options unusedPublished trigger path is not reachedנמוך
Options appear only in dead codeConfirm production build and runtime reachabilityLow to medium
Options used but handle is never resetPublished sequence remains incompleteMedium investigation
Options plus reset and cleanup are reachableVulnerability is technically reachableHigh remediation priority
Untrusted input affects lifecyclePotential remote or cross-tenant triggerUrgent analysis
Reachable in a privileged serviceCrash or memory corruption may have major impactUrgent remediation
Reachable only in a local test utilityLimited operational exposureNormal patch cycle

The purpose of this matrix is not to reduce every finding to Low. It is to ensure that priority reflects deployed reality.

An internet-facing service using the affected options in attacker-influenced request workflows deserves substantially more urgency than an internal application that includes libcurl but never calls the dependency APIs.

How to Inventory CVE-2026-10536 Safely

Safe validation should begin with passive evidence collection. Do not begin by running an untrusted crash reproducer against a production service.

Check system packages

On Debian or Ubuntu:

dpkg-query -W -f='${Package} ${Version}\n' \
  curl libcurl4 libcurl4-openssl-dev 2>/dev/null

apt-cache policy curl libcurl4

On RPM-based systems:

rpm -qa | grep -E '^curl|^libcurl'

rpm -q --changelog libcurl |
  grep -i 'CVE-2026-10536'

On Alpine:

apk info -vv |
  grep -E '^(curl|libcurl)-'

On macOS with Homebrew:

brew list --versions curl
brew info curl

Package information is useful, but it represents only one layer. An application may bundle its own copy or link libcurl statically.

Inspect dynamic dependencies

בלינוקס:

ldd /path/to/application |
  grep -i curl

readelf -d /path/to/application |
  grep -i curl

On macOS:

otool -L /path/to/application |
  grep -i curl

On Windows:

dumpbin /DEPENDENTS application.exe |
  findstr /I curl

These commands can identify expected dynamic dependencies, but runtime search paths, container isolation, private loaders and environment variables may still influence which library is loaded.

Inspect the running process

On Linux, the maps file can reveal the actual shared object loaded into a running process:

grep -i curl /proc/PID/maps

Alternatively:

lsof -p PID |
  grep -i curl

This evidence is often more reliable than inspecting only the executable because it reflects the process’s resolved runtime dependency.

Detect the Runtime libcurl Version

Applications using libcurl directly can query the library loaded into the process through curl_version_info().

The official API exposes the runtime version, numeric version and feature mask. The CURL_VERSION_HTTP2 feature bit indicates whether the build includes HTTP/2 support. (תלתל)

A small inventory helper can collect this evidence:

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
    curl_version_info_data *info =
        curl_version_info(CURLVERSION_NOW);

    if(!info) {
        fprintf(stderr,
                "Unable to query libcurl information\n");
        return 1;
    }

    printf("libcurl version: %s\n",
           info->version ? info->version : "unknown");

    printf("version number: 0x%06x\n",
           info->version_num);

    printf("HTTP/2 support: %s\n",
           (info->features & CURL_VERSION_HTTP2)
               ? "yes"
               : "no");

    if(info->nghttp2_version) {
        printf("nghttp2 version: %s\n",
               info->nghttp2_version);
    }

    return 0;
}

Compile it using the same compiler and library-resolution environment used by the target application:

cc curl_runtime_info.c \
  $(pkg-config --cflags --libs libcurl) \
  -o curl_runtime_info

This program is an inventory tool. It does not configure stream dependencies, reset handles or exercise the vulnerable cleanup sequence.

Search the Source Code for Affected API Usage

Source-level reachability is particularly useful for CVE-2026-10536 because the two vulnerable options have distinctive names.

A repository-wide search can begin with:

rg -n \
  'CURLOPT_STREAM_DEPENDS(_E)?|curl_easy_reset|curl_easy_cleanup' \
  .

Using standard grep:

grep -RInE \
  'CURLOPT_STREAM_DEPENDS(_E)?|curl_easy_reset|curl_easy_cleanup' \
  .

Finding only curl_easy_reset() is not enough. Many legitimate applications reset easy handles without using HTTP/2 stream dependencies.

The strongest source-level indicator is the presence of:

CURLOPT_STREAM_DEPENDS
CURLOPT_STREAM_DEPENDS_E

After locating those calls, review the complete lifecycle of the related handles.

Important questions include:

  1. Which easy handle is the parent?
  2. Which easy handle is the child?
  3. Does a wrapper hide the dependency option?
  4. Can an error path call curl_easy_reset()?
  5. Does a retry pool reset handles before reuse?
  6. Are handles later destroyed with curl_easy_cleanup()?
  7. Can timeout or cancellation change the cleanup order?
  8. Is the relevant code included in the production build?
  9. Can remote input influence request creation or cancellation?
  10. Is the affected code reachable in privileged or multi-tenant processes?

A pattern such as the following deserves immediate review:

CURL *parent = curl_easy_init();
CURL *child = curl_easy_init();

curl_easy_setopt(child,
                 CURLOPT_STREAM_DEPENDS,
                 parent);

/* Transfer or error-handling logic */

curl_easy_reset(child);

/* Later shutdown path */

curl_easy_cleanup(child);
curl_easy_cleanup(parent);

This example illustrates a potentially relevant lifecycle. It does not prove exploitability on its own. The deployed libcurl version, actual execution order, internal relationship and attacker influence still need to be established.

Review Wrappers, Bindings and Vendored Code

Many applications do not call libcurl directly.

The vulnerable APIs may appear inside:

  • C or C++ networking wrappers
  • Browser engines
  • Download managers
  • Embedded SDKs
  • Package managers
  • Media-transfer software
  • Language-native extensions
  • Generated foreign-function interfaces
  • Vendor forks
  • Privately maintained libcurl abstractions

A source scan limited to the main application directory may miss the relevant code in a dependency.

ביקורת:

vendor/
third_party/
external/
deps/
bindings/
native/
ffi/
submodules/

Also inspect build manifests to determine whether the matching source is actually compiled into the released product.

For a stripped binary without source code, strings can provide weak supporting evidence:

strings /path/to/application |
  grep -E 'CURLOPT_STREAM_DEPENDS(_E)?'

The absence of readable strings does not prove safety. The option may be represented by a numeric constant, optimized away, hidden in another library or stripped during compilation.

Validate with AddressSanitizer

The curl advisory states that AddressSanitizer detects the invalid access. A safe validation process should use an isolated laboratory environment rather than a production workload. (תלתל)

Compile the consuming application and an affected libcurl build with:

export CFLAGS="-O1 -g \
-fsanitize=address,undefined \
-fno-omit-frame-pointer"

export CXXFLAGS="$CFLAGS"
export LDFLAGS="-fsanitize=address,undefined"

Run the application’s own integration tests that exercise:

  • HTTP/2 multiplexing
  • Related transfers
  • Retry handling
  • Cancellation
  • Timeout paths
  • Handle pooling
  • Handle reset
  • Application shutdown
  • Error cleanup
  • Worker destruction

Product-native workflows are preferable to a generic external reproducer because they show whether the vulnerable path is reachable through the application’s real state machine.

A high-quality AddressSanitizer result should record:

  • The exact application build
  • The libcurl version and source revision
  • Compiler version
  • Build flags
  • HTTP/2 backend
  • Complete sanitizer trace
  • Triggering test workflow
  • Handle relationship
  • Whether the finding disappears after patching

An ASan trace against a synthetic harness proves that the upstream bug exists. It does not automatically prove that a deployed application exposes the same trigger.

Validate with Valgrind

For a compatible Linux build:

valgrind \
  --tool=memcheck \
  --leak-check=full \
  --track-origins=yes \
  --error-exitcode=99 \
  ./application-test-suite

Valgrind can significantly slow execution and alter timing. It belongs in a controlled test environment or CI pipeline, not in a production request path.

A useful Valgrind finding should answer:

  • Which application action produced the invalid access?
  • Had CURLOPT_STREAM_DEPENDS או _E been configured?
  • Which handle was reset?
  • Which cleanup operation followed?
  • Was the parent still alive?
  • Did the application crash without Valgrind?
  • Does the patched build eliminate the report?
  • Are any unrelated memory errors present?

This information turns a generic memory warning into actionable evidence.

What Defenders Can Detect in Production

CVE-2026-10536 does not have a universal network signature.

A WAF or IDS cannot determine from HTTP/2 frames alone whether a client application has internally created a dependency between two libcurl handles, reset one of them and retained stale cleanup state.

The same HTTP/2 traffic may be safe in one application and dangerous in another.

Production detection should therefore focus on four evidence categories.

Component evidence

Track:

  • libcurl package revision
  • Runtime shared-library path
  • Static-linking provenance
  • Container image digest
  • SBOM component version
  • Vendor patch status
  • Build commit
  • Presence of the official fix

Source or binary evidence

Search for:

CURLOPT_STREAM_DEPENDS
CURLOPT_STREAM_DEPENDS_E
curl_easy_reset
curl_easy_cleanup

The dependency options are the strongest initial indicators because they are required by the published trigger.

Crash telemetry

Investigate crashes involving affected libcurl builds, especially when stack traces reference:

  • Easy-handle cleanup
  • HTTP/2 priority state
  • Stream dependency state
  • Parent-child tracking
  • Final transfer destruction
  • Reset-related cleanup

Useful telemetry includes:

  • Core dumps
  • Native crash reports
  • ASan reports
  • Assertion messages
  • Worker restart counts
  • Service termination events
  • Request state before the crash
  • Recent retries or cancellations
  • Loaded libcurl library path

A crash does not automatically prove malicious exploitation. It may nevertheless demonstrate that the product reaches the defective lifecycle.

Behavioral evidence

Determine whether an external user can repeatedly cause:

  • Transfer cancellation
  • Handle-pool churn
  • Worker destruction
  • Connection teardown
  • Request retries
  • Process crashes
  • Service restarts

A repeatable externally influenced crash substantially increases operational priority, even if the upstream project rates the underlying flaw Low.

Remediation Option One: Upgrade to curl 8.21.0 or Later

The preferred upstream remediation is to upgrade curl and libcurl to version 8.21.0 or later. (תלתל)

After upgrading, verify the library actually used by the application. Updating /usr/bin/curl does not prove that a private, bundled or statically linked application has changed.

A complete validation should include:

Package or build record
Runtime libcurl version
Application restart or redeployment
Loaded library path
Source review for deprecated options
HTTP/2 regression tests
Re-execution of previous sanitizer tests
Confirmation that the old binary is absent

Remediation Option Two: Apply the Official Patch

Products that cannot immediately migrate to 8.21.0 can backport the official fix and rebuild libcurl.

The upstream fix does more than add a defensive pointer check. It removes HTTP/2 stream-dependency tracking and converts the two options into no-ops.

The commit explains that the feature is deprecated, few servers implement it and the internal implementation is complicated by state management. The option declarations and documentation are marked deprecated beginning with 8.21.0. (GitHub)

Using the upstream patch is preferable to creating a narrow local workaround around one observed crash. A one-line null check might suppress a particular fault while leaving other stale relationships or lifecycle inconsistencies intact.

Document the following when backporting:

  • Original curl version
  • Applied commit
  • Local patch identifier
  • Merge conflicts
  • Build configuration
  • HTTP/2 backend
  • Test results
  • Package revision
  • Deployment scope
  • Rollback plan

Remediation Option Three: Stop Using Stream Dependencies

The official mitigation is to avoid configuring HTTP/2 stream dependencies. (Red Hat Customer Portal)

Remove calls to:

CURLOPT_STREAM_DEPENDS
CURLOPT_STREAM_DEPENDS_E

This mitigation aligns with the evolution of HTTP/2. RFC 9113 deprecated the original priority and dependency signaling, and curl 8.21.0 no longer implements the dependency behavior. (עורך RFC)

Applications should still perform regression testing. Although many servers ignored the old dependency signals, a private or specialized server might have used them.

Test:

  • Request completion time
  • Critical-resource latency
  • Transfer fairness
  • Multiplexing behavior
  • Cancellation
  • Connection reuse
  • CPU consumption
  • Memory consumption
  • Server-side scheduling
  • User-visible performance

The security fix should not be considered complete until functional and performance behavior has been evaluated.

What Changes in curl 8.21.0?

Starting with curl 8.21.0, calls such as:

curl_easy_setopt(child,
                 CURLOPT_STREAM_DEPENDS,
                 parent);

and:

curl_easy_setopt(child,
                 CURLOPT_STREAM_DEPENDS_E,
                 parent);

remain recognized for compatibility, but they no longer establish HTTP/2 dependency relationships.

The official patch documentation says that setting the options no longer has an effect and that both options are deprecated as of version 8.21.0. The associated enum declarations describe them as having no function. (GitHub)

This design reduces immediate compatibility breakage for applications that still compile against the options. However, software that depended on the old prioritization behavior should remove those assumptions from its architecture.

Do You Need to Disable HTTP/2?

Generally, no.

CVE-2026-10536 does not require defenders to disable HTTP/2 entirely. The vulnerable behavior concerns deprecated stream-dependency options and their interaction with reset and cleanup.

Disabling HTTP/2 could introduce significant performance and compatibility changes while addressing a much narrower problem than necessary.

A proportionate response is:

  1. Upgrade or backport the fix.
  2. Remove use of the dependency options.
  3. Verify handle lifecycle behavior.
  4. Retest HTTP/2 multiplexing.
  5. Disable HTTP/2 only when no supported patch or application-level mitigation is available and the vulnerable path is confirmed.

Static Linking and Hidden libcurl Copies

Static linking is a common reason that CVE-2026-10536 may remain after a system package update.

Consider an application built with:

cc application.c \
  /opt/vendor/lib/libcurl.a \
  -o application

The resulting executable may contain libcurl code directly. Updating the system’s libcurl.so does not change that executable.

Indicators of static or private linking include:

  • No libcurl entry in ldd
  • libcurl symbols inside the executable
  • Large monolithic binaries
  • Vendor-specific library directories
  • Build manifests referencing libcurl.a
  • SBOM results identifying embedded curl code
  • Runtime behavior inconsistent with the system package
  • Container images containing their own library copy

For statically linked products, the durable remediation is to rebuild and redeploy the application with patched curl source.

The same principle applies to containers. Updating the host operating system does not update libcurl inside an existing image. The image must be rebuilt from a patched base or with an updated dependency, then redeployed and verified through its new digest.

Language Bindings and Frameworks

libcurl is used through many language bindings and application frameworks.

The dependency chain may look like:

Application
    ↓
Language HTTP library
    ↓
Native extension
    ↓
libcurl

A Python, PHP, Ruby, Rust, Java or .NET application should not be marked reachable merely because one of its dependencies can use libcurl.

The key question is whether the binding exposes or internally uses the two stream-dependency options.

Conversely, searching only the application’s top-level source code may miss vulnerable behavior in a native extension.

A complete investigation should identify:

  • The exact binding
  • Its native source
  • The linked libcurl version
  • Whether HTTP/2 is enabled
  • Whether dependency options are exposed
  • Whether the application uses them
  • Whether reset and cleanup are reachable
  • Whether untrusted input controls the lifecycle

Containers and Distroless Images

Package-manager commands are often insufficient for minimal or distroless containers.

A conventional image can be inspected with:

docker run --rm IMAGE_NAME sh -c '
  (curl --version || true) &&
  (apk info -vv 2>/dev/null | grep -E "curl|libcurl" || true) &&
  (dpkg-query -W 2>/dev/null | grep -E "curl|libcurl" || true) &&
  (rpm -qa 2>/dev/null | grep -E "curl|libcurl" || true)
'

A distroless image may not contain a shell or package database. In that case, use:

  • SBOM generation
  • Build provenance
  • Layer inspection
  • Binary dependency analysis
  • Debug variants
  • Source lockfiles
  • Compiler link maps
  • Vendor manifests
  • Runtime process maps

Do not interpret the absence of a package-manager result as proof that libcurl is absent.

Enterprise Remediation Priorities

Although curl rates CVE-2026-10536 Low, upgrading is still sensible because a fix is available and memory-safety bugs can behave unpredictably.

Priority One: Confirmed reachable internet-facing services

Patch urgently when:

  • An affected libcurl build is present.
  • One of the dependency options is used.
  • Reset and cleanup are reachable.
  • Untrusted input can affect transfer lifecycle.
  • The process is internet-facing, privileged or multi-tenant.

Priority Two: Internal products using the affected options

Patch in an expedited maintenance window when the APIs and lifecycle are reachable, even if no external trigger has been confirmed.

The application may still suffer reliability failures, and deeper testing may reveal attacker influence that was initially overlooked.

Priority Three: Affected component without API usage

Upgrade through normal security maintenance. Preserve source or binary evidence demonstrating that the required dependency options are not used in the shipped build.

Priority Four: Version-only scanner findings

Before escalating a version match as Critical, verify:

  • Vendor backports
  • Runtime library resolution
  • Static linking
  • Container contents
  • HTTP/2 feature support
  • Dependency-option usage
  • Handle lifecycle
  • Attacker influence

This approach gives vulnerability-management teams an auditable reason for their priority.

Safe CVE Validation Requires Evidence, Not Just a Version Match

A defensible validation workflow should establish five layers:

Component identity
        ↓
Affected code presence
        ↓
Application-level reachability
        ↓
Observable security impact
        ↓
Patch effectiveness

An SBOM match establishes component presence. It does not establish that the application uses the vulnerable feature.

A source-code match establishes potential API usage. It does not establish that the code is part of the production build.

A sanitizer crash establishes technical reachability in a particular test. It does not automatically establish remote attacker control.

A repeatable externally influenced application crash provides much stronger evidence of security impact.

For CVE-2026-10536, a high-quality security report should contain:

  • Exact libcurl build
  • Source or package provenance
  • HTTP/2 feature status
  • Evidence of dependency-option usage
  • Handle relationship
  • Reset-and-cleanup execution path
  • Sanitizer or crash trace
  • Attacker-influence analysis
  • Process privileges
  • Operational consequence
  • Fixed-build comparison
  • Remaining uncertainty

How Penligent Can Support CVE Validation

Component scanners are valuable for discovering potentially affected libcurl versions, but a version match should be treated as the start of an investigation rather than the final result.

For a vulnerability such as CVE-2026-10536, an evidence-driven pentesting workflow should combine software inventory, source or binary analysis, application behavior, controlled runtime testing and post-fix verification.

Within an authorized environment, a Penligent-based workflow can begin with the target application and its known vulnerability summary, then guide the main agent toward the relevant evidence:

Identify the deployed libcurl instance
        ↓
Confirm affected version or missing patch
        ↓
Inspect HTTP/2 support
        ↓
Search for stream-dependency API usage
        ↓
Map reset and cleanup paths
        ↓
Validate safely in an isolated environment
        ↓
Collect sanitizer or crash evidence
        ↓
Retest after remediation

This is especially important when public scores conflict. Instead of repeating the highest CVSS number, the final report can distinguish component presence, vulnerable code reachability, attacker influence and demonstrated impact.

For CVE-2026-10536, the most valuable result is not merely “libcurl 8.20.0 detected.” It is a supported conclusion such as:

The application loads libcurl 8.20.0 and has HTTP/2 enabled,
but neither CURLOPT_STREAM_DEPENDS nor
CURLOPT_STREAM_DEPENDS_E is present in the production code.
The published vulnerable path is therefore not currently
reachable based on the reviewed evidence.

Or, in a higher-risk case:

The application configures CURLOPT_STREAM_DEPENDS between
two easy handles. A remote cancellation workflow causes the
child handle to be reset and later destroyed. An ASan build
reproduces a heap-use-after-free in the cleanup path.
The issue no longer reproduces after applying the upstream fix.

The second conclusion is significantly more actionable than a generic CVE alert because it connects a component defect to a real application path and verifies the remediation.

Common Validation Mistakes

Treating curl --version as complete evidence

The command reports the CLI build. Another application may load a different shared library or contain a static copy.

Treating an affected version as exploitable

The application must use one of the rare dependency options and follow the relevant lifecycle.

Calling the issue universal remote RCE

The public material establishes a use-after-free and potential application crash. A reliable remote-code-execution chain requires separate product-specific evidence.

Calling 9.8 the NVD score

The NVD page states that NVD has not yet supplied its own score. The displayed 9.8 comes from CISA-ADP. (NVD)

Ignoring vendor backports

Enterprise packages may retain an older upstream-looking version while containing the official fix.

Running a public reproducer in production

A memory-corruption test can crash the process, disrupt service or damage application state. Use an isolated, instrumented environment.

Patching only the host

Containers, static binaries, private runtimes and embedded SDKs require separate updates.

Ignoring compatibility after the upgrade

The two dependency options become no-ops in curl 8.21.0. Applications that expected their old behavior should undergo regression testing.

Frequently Asked Questions

What is CVE-2026-10536?

CVE-2026-10536 is a use-after-free vulnerability in libcurl’s HTTP/2 stream-dependency implementation. It can occur when an application configures CURLOPT_STREAM_DEPENDS או CURLOPT_STREAM_DEPENDS_E, resets a related easy handle and later destroys it. (תלתל)

Which versions are affected?

Upstream curl and libcurl versions 7.88.0 through 8.20.0 are affected. Versions earlier than 7.88.0 and versions 8.21.0 or later are outside the affected upstream range. Vendor backports should be checked separately. (תלתל)

Is the curl command-line tool vulnerable?

No. The curl project explicitly states that the command-line tool is not affected. The flaw concerns applications that use the relevant libcurl APIs. (תלתל)

Is every application using libcurl vulnerable?

No. The application must use CURLOPT_STREAM_DEPENDS או CURLOPT_STREAM_DEPENDS_E and enter the relevant reset-and-cleanup lifecycle.

Is every HTTP/2-enabled libcurl build exploitable?

No. HTTP/2 support is only one prerequisite. The application must also use the deprecated dependency APIs and follow the vulnerable lifecycle.

Can a malicious HTTP/2 server trigger the bug automatically?

Not in an arbitrary libcurl application. The application itself must configure the dependency relationship and perform the relevant lifecycle calls. A remote trigger may be possible in a specific product if untrusted input controls transfer creation, cancellation, reset or cleanup.

Is CVE-2026-10536 an RCE vulnerability?

The verified public material establishes a use-after-free, memory-safety errors, assertion failures and possible application crashes. It does not establish a universal remote-code-execution chain against all affected libcurl applications.

Why does NVD display a 9.8 score?

NVD displays a 9.8 Critical score contributed by CISA-ADP. NVD’s page states that its own assessment has not yet been provided. (NVD)

Why does curl rate it Low?

The required options are extremely rarely used, HTTP/2 stream dependencies are deprecated, the curl command-line tool is unaffected and a precise application lifecycle is required. (תלתל)

Why does Red Hat rate it Moderate?

Red Hat evaluates the issue in the context of its own products and considers application crash and denial of service the realistic impact. It assigns a CVSS 3.1 score of 4.7 and notes that a specific programming pattern is required. (Red Hat Customer Portal)

Is there public proof-of-concept information?

The NVD change record identifies the related HackerOne report as an exploit reference and records exploitation: poc in the CISA SSVC data. This establishes the existence of reproduction information, not universal exploitability across all consuming applications. (NVD)

Can an IDS or WAF detect exploitation?

There is no universal network signature because the essential failure occurs in the client application’s internal handle lifecycle.

Can the issue be mitigated without upgrading?

Yes. Stop using CURLOPT_STREAM_DEPENDS ו CURLOPT_STREAM_DEPENDS_E. Avoiding HTTP/2 stream dependencies is one of the official mitigation options. (תלתל)

Must HTTP/2 be disabled?

Usually not. Removing the dependency options or installing the official fix is more targeted.

What happens to the options in curl 8.21.0?

They remain recognized for compatibility but no longer create stream dependencies. They are marked deprecated and have no functional effect. (GitHub)

Why did curl remove the feature instead of repairing it?

The upstream commit explains that the feature was deprecated, few servers implemented it and the internal state management was complicated. (GitHub)

How can I determine whether HTTP/2 is enabled?

שימוש curl_version_info() and check the CURL_VERSION_HTTP2 feature bit. (תלתל)

Does updating the system curl package fix static applications?

No. A statically linked application must be rebuilt with patched curl source. Containers and bundled library copies must also be updated separately.

How should I confirm the fix?

Confirm the runtime library or vendor package revision, inspect the deprecated option usage, rerun HTTP/2 lifecycle tests and verify that the previous sanitizer or crash result no longer occurs.

Final Assessment

CVE-2026-10536 is a genuine libcurl memory-safety vulnerability, but its practical scope is substantially narrower than a generic “critical remote HTTP/2 vulnerability” headline suggests.

The vulnerable upstream code exists in curl 7.88.0 through 8.20.0. Triggering it requires an application to use one of two uncommon and deprecated stream-dependency options, reset a relevant easy handle and later enter the vulnerable cleanup path. The curl command-line tool is not affected. (תלתל)

The public severity disagreement should be explained rather than hidden. curl classifies the issue as Low. Red Hat rates its product impact Moderate with CVSS 4.7. Debian describes it as a minor issue requiring a rare programming pattern. NVD displays a CISA-ADP score of 9.8 Critical while stating that NVD has not yet provided its own score. (Debian Security Tracker)

Those ratings reflect different assumptions about reachability and impact. They do not describe four different vulnerabilities.

For defenders, the correct response is neither panic nor dismissal. Inventory the actual libcurl instances, confirm runtime versions, check vendor backports, search for the affected APIs, inspect reset-and-cleanup behavior and validate reachable paths with AddressSanitizer or Valgrind in an isolated environment.

The preferred remediation is curl 8.21.0 or a vendor package containing the official fix. Applications should remove assumptions about the old dependency behavior because the two options are deprecated and function as no-ops after the fix. (תלתל)

The broader engineering lesson extends beyond curl. Reset operations are dangerous when object state includes cross-object relationships. Clearing one object’s local configuration is not sufficient when parents, children, queues, caches or callbacks still retain references to that state.

Every reset path must preserve the invariants required by later reuse and eventual destruction.

CVE-2026-10536 is therefore best understood as a cleanup-path and object-lifecycle integrity failure—not as evidence that every HTTP/2 application using libcurl is remotely exploitable.

שתף את הפוסט:
פוסטים קשורים
he_ILHebrew