Skip to content

CVE-2026-92392: Verified Reproduction

CVE-2026-92392: curl HTTP/2 upload Use-After-Free RST STREAM HTTP 1 1 REQUIRED/REFUSED STREAM leading to potential RCE

CVE-2026-92392 is verified against the affected target. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00386.

REPRO-2026-00386 RCE Oct 14, 2026 CVE entry .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
305m 45s
Tool calls
846
Spend
$234.97
01 · Overview

What Is CVE-2026-92392?

CVE-2026-92392 is a high-severity RCE vulnerability. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00386).

02 · Severity & CVSS

CVE-2026-92392 Severity

CVE-2026-92392 is rated high severity.

HIGH threat level
Weakness CWE-416 Use After Free — Use After Free

High — serious impact or readily exploitable. Prioritize remediation.

How to Reproduce CVE-2026-92392

$ pruva-verify REPRO-2026-00386
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00386/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-92392

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • crash observed
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

A malicious TLS/HTTP/2 TCP peer controls flow-control stalls, REFUSED_STREAM resets, HPACK headers, TLS session tickets used for heap grooming, resurrected-stream window updates, and the final fake Curl_easy callback trigger while the victim runs curl -Z -T on a 586035-byte file.

Attack chain
  1. RST_STREAM retry
  2. vulnerable multi_done ordering flushes queued upload bytes as a new h2 request
  3. orphaned stream retains freed Curl_easy pointer
  4. TLS-ticket/HPACK heap grooming reclaims and corrupts it
  5. forged-stream over-read discloses current libc/libcurl bases
  6. fake Curl_easy sets fdebug to fresh libc system()
  7. invalid frame callback executes id>mk_<nonce> in the curl process
How the agent worked 1,731 events · 846 tool calls · 5h 21m
5h 21mDuration
846Tool calls
213Reasoning steps
1,731Events
36Dead-ends
Agent activity over 5h 21m
Policy
2
Support
24
Repro
1,300
Judge
68
Variant
329
Verify
1
0:00320:33

Root Cause and Exploit Chain for CVE-2026-92392

Versions: package/component: curl/libcurl HTTP/2 upload and retry handling with nghttp2.Fixed: control: commit 8b0744af5b1b631da5b938ca31c3f9895ad5a321 (multi: adjust the cleanup order in multi_done).

curl 8.22.0 contains a use-after-free in the HTTP/2 upload retry path. When a malicious HTTP/2 peer resets an active upload with HTTP_1_1_REQUIRED or REFUSED_STREAM, libcurl converts the reset into a retry. In the vulnerable cleanup order, curl first tells the connection filters that the transfer is done and then lets request cleanup flush buffered upload bytes. Those bytes can be reinterpreted as a new HTTP/2 request, creating an unintended stream whose user data is the soon-to-be-freed Curl_easy. This run reproduced the complete disclosed exploit chain through the real curl tool: a malicious peer reclaimed and corrupted the freed object, induced a 99,056-byte forged-stream heap over-read, recovered fresh current-process libc and libcurl addresses despite ASLR, replaced the object with a fake Curl_easy, and triggered its attacker-selected debug callback as libc system(), creating a verifiable target-local marker.

  • Affected package/component: curl/libcurl HTTP/2 upload and retry handling with nghttp2.
  • Affected versions: curl 8.22.0 and earlier according to the disclosed advisory. The vulnerable target used here is the exact curl 8.22.0 commit 01346829096c61b372692f6dc43ffa778c6caccd.
  • Fixed control: commit 8b0744af5b1b631da5b938ca31c3f9895ad5a321 (multi: adjust the cleanup order in multi_done).
  • Risk level: High. A malicious HTTP/2 server can execute a command in the victim curl process when the victim uploads a suitable file. The exploit needs a large upload containing request-like text; the demonstrated ASLR-enabled chain also uses curl parallel mode (-Z) and a heap-layout-calibrated runtime.
  • Consequence: Remote command execution with the privileges and confinement of the curl process. The marker commands in the final isolated proof were echo Vv1>mk_none and echo Vv2>mk_none.

Impact Parity

  • Disclosed/claimed maximum impact: Remote code execution (code_execution).
  • Reproduced impact from this run: Attacker-induced command execution through the production curl CLI and TCP/TLS/HTTP/2 boundary. Two fresh vulnerable processes created unique distinct marker files after independently leaking their randomized libc/libcurl bases. Two fixed processes reached the same malicious reset/stall path but created no resurrected stream, disclosure, callback trigger, or marker.
  • Parity: full.
  • Not demonstrated: The marker command is intentionally harmless and does not establish persistence or privilege escalation. Reliability outside the pinned exploit runtime was not claimed; the underlying UAF is broader than this heap-grooming calibration.

Root Cause

The failure is an ordering bug in multi_done() combined with HTTP/2 stream user data retaining a raw Curl_easy *:

  1. curl uploads a large file over HTTP/2. The peer limits stream flow control so an upload remainder stays queued.
  2. The malicious peer replies and sends RST_STREAM(HTTP_1_1_REQUIRED or REFUSED_STREAM), causing libcurl to schedule a retry.
  3. Vulnerable multi_done() calls Curl_conn_ev_data_done(data, premature) before Curl_req_done(&data->req, data, premature).
  4. Connection-filter teardown makes curl forget the original transfer/stream association. Request teardown then flushes the buffered upload remainder into the still-live connection.
  5. If those file bytes parse as an HTTP request head, h2_submit() opens a new/resurrected stream and nghttp2 stores the raw Curl_easy * as stream user data.
  6. The normal retry completes. The curl tool frees the easy handle, but the pooled HTTP/2 connection and resurrected stream retain the stale pointer.
  7. The exploit uses attacker-controlled TLS 1.3 session-ticket sizes and HPACK header allocations to reclaim the freed 3,368-byte Curl_easy, then corrupt a buf_chunk associated with the orphaned stream. A window update makes curl transmit bytes outside the intended upload buffer.
  8. The peer receives 99,056 leaked heap bytes and identifies main_arena and libcurl pointers. Those per-process values resolve libc system() under ASLR.
  9. A final session ticket reoccupies the stale object with a fake Curl_easy: the command string begins at offset zero, set.fdebug points to the freshly resolved system(), and the verbose flag forces the callback path. An invalid HTTP/2 frame invokes Curl_failf()/tracing through the fake callback, executing the marker command.

The disclosed exploit was originally calibrated with ZT_V1=34660, which leaked only upload prose in this runtime. Live heap analysis showed the current unsorted-piece geometry was smaller; changing only that first carve value to ZT_V1=32400 preserved the intended 0xc50 remainder. Adjacent controls (32160 and 32640) failed to disclose pointers, while 32400 succeeded repeatedly.

The upstream fixed commit is https://github.com/curl/curl/commit/8b0744af5b1b631da5b938ca31c3f9895ad5a321. It moves Curl_req_done() before Curl_conn_ev_data_done() so request bytes are finished while the connection filter still knows the transfer, and then explicitly tells filters to give up references before detaching the connection. In this run, the fixed build never created the resurrected stream.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh from any directory. PRUVA_ROOT may be set to the bundle path.

  2. The script resolves the prepared project cache, verifies the exact vulnerable and fixed source commits and cleanup-order hunk, and verifies or rebuilds a pinned Ubuntu 24.04 exploit image containing stock non-ASAN curl 8.22.0, nghttp2 1.59.0, OpenSSL 3.0.13, and glibc 2.39.

  3. It builds the exact fixed commit with the same compiler, flags, dependencies, and runtime closure.

  4. For each attempt, the script starts the real malicious TLS/HTTP/2 TCP peer and invokes the actual curl tool as:

    curl -Z -T sample_docs.md https://127.0.0.1:<port>/x https://127.0.0.1:<port>/y
    
  5. It runs two isolated vulnerable attempts and requires each to show a resurrected stream, a full 115,439-byte forged transfer, a 99,056-byte leaked-memory artifact, independently resolved libc/libcurl bases, the final fake-object callback trigger, and an actual marker.txt copied from the curl process's working directory.

  6. It runs two isolated fixed attempts with the same peer and packet sequence and requires each to reach the HTTP/2 reset/stall path without resurrection, disclosure, callback trigger, or marker.

  7. It verifies that the two vulnerable libc/libcurl bases differ, proving that ASLR is active and that addresses were reacquired from each live process rather than reused as constants.

  8. Expected final output is:

    [+] CONFIRMED: malicious HTTP/2 peer achieved command execution in curl 8.22.0 (2/2); fixed commit blocked the chain (2/2)
    

Evidence

The final script was run twice consecutively and passed both times. The latest immutable proof is bound by bundle/repro/runtime_manifest.json.

Vulnerable process 1
  • Peer transcript: bundle/repro/rce-vuln-1/server.log

  • curl transcript: bundle/repro/rce-vuln-1/victim.log

  • Leaked bytes: bundle/repro/rce-vuln-1/leak.bin (99,056 bytes)

  • Actual command marker: bundle/repro/rce-vuln-1/marker.txt

  • Marker value:

    Vv1
    
  • Key peer evidence:

    *** RESURRECTED stream 5 ...
    Z9: pulled 115439 bytes of DATA on S'=5 (wanted 115439)
    Z9: leaked 99056 bytes
    Z9: libc_base=0x7f8bb0400000 by 5 vote(s) ... system=0x7f8bb0458750; libcurl_base=0x7f8bb0e4b000
    Z10: sent NST(final fake, 2832 B, fdebug=0x7f8bb0458750, cmd=b'echo Vv1>mk_none') ...
    
Vulnerable process 2
  • Peer transcript: bundle/repro/rce-vuln-2/server.log

  • Leaked bytes: bundle/repro/rce-vuln-2/leak.bin (99,056 bytes)

  • Marker: bundle/repro/rce-vuln-2/marker.txt

  • Independently randomized/disclosed addresses:

    libc_base=0x7fa11e705000
    libcurl_base=0x7fa11f150000
    system=0x7fa11e75d750
    

These differ from vulnerable process 1. /proc/sys/kernel/randomize_va_space was 2; the exploit did not read /proc in the final proof.

Fixed negative controls
  • bundle/repro/rce-fixed-1/server.log
  • bundle/repro/rce-fixed-2/server.log
  • bundle/repro/rce-fixed-1/victim-cwd-ls.txt
  • bundle/repro/rce-fixed-2/victim-cwd-ls.txt

Both controls show the listener, TLS/HTTP/2 preface, 586,035-byte upload, first reset, retry, and second stalled upload. For example:

conn0: learned 586035 body bytes ... RST_STREAM(REFUSED_STREAM)
conn1: A#2 stalled at 360447 body bytes -> trigger refused

Neither control contains RESURRECTED, Z9, or Z10; each victim directory is empty and marker-name.txt contains only a newline.

Build and mitigation identity
  • bundle/logs/repro/rce-vulnerable-identity.log: curl 8.22.0 executable/library/libc checksums and version.
  • bundle/logs/repro/rce-vulnerable-mitigations.log: PIE (ET_DYN), NX stack, RELRO, symbols, build ID, and loader resolution.
  • bundle/logs/repro/rce-fixed-mitigations.log: corresponding fixed-build evidence.
  • bundle/logs/repro/randomize_va_space.log: kernel ASLR setting.
  • Vulnerable executable SHA-256: ee31384e2df166f57d7dbdf3f8a7f9f368a38f8ddcf8ef4e80ca038c06931ae7.
  • Vulnerable libcurl SHA-256: ee986196347fe90ba808c6fa5dcde03ce4d27a08a16fa339b454a74e6785ef53.
  • glibc SHA-256: d8db8739a1633c972cec6a4fe0566bdcec6fd088f98723492ab0361f66238f75.
  • Fixed ELF SHA-256: 216e8087e5b4a496d76eb3133687c6a748ce7da1dc7cc808de7a1e001393d68f.
  • Fixed libcurl SHA-256: 74b322c9f6492924604a8546d3c0302a8be05f02a544b5f0425c2ff7c334f53d.

Recommendations / Next Steps

  • Upgrade to curl 8.23.0 or another release containing commit 8b0744af5b1b631da5b938ca31c3f9895ad5a321.
  • For backports, ensure Curl_req_done() occurs before Curl_conn_ev_data_done() and that connection filters release all Curl_easy references before the object can be detached/freed.
  • Add regression tests for both HTTP_1_1_REQUIRED and REFUSED_STREAM, large flow-controlled uploads, retry paths, request-like patterns at multiple upload offsets, parallel transfers, connection-pool shutdown, and frame delivery to stale stream IDs.
  • Exercise those tests with ASAN and non-sanitized hardened builds.
  • Consider replacing raw Curl_easy * nghttp2 stream user data with a validated identifier/lookup so a stale stream cannot directly retain an object address.

Additional Notes

  • The final production proof uses no sanitizer and keeps certificate verification enabled. The malicious peer certificate is signed by an isolated test CA installed in the pinned container.
  • The reproducer is idempotent and passed twice consecutively, with two fresh vulnerable and two fresh fixed processes per invocation.
  • The vulnerability trigger itself is not tied to this exact heap. Full command execution is allocator/build sensitive, so the reproducer pins the target closure and verifies its binary hashes before running.
  • Later glibc double free or corruption aborts are expected after the marker has been created because the exploit deliberately corrupts allocator/object state. Marker creation and fixed negative controls, not the final abort, are the command-execution oracle.
  • The final bounded marker commands are echo Vv1>mk_none and echo Vv2>mk_none; each is at most 23 bytes because the fake object's multi field begins at offset 24. Fixed attempts receive distinct candidate commands echo Vf1>mk_none and echo Vf2>mk_none, but create no file.

CVE-2026-92392 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:45
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-92392 · REPRO-20
0:09
0:10
0:12
web search
0:19
0:20
1:10
1:10
extract_facts
no facts extracted
1:14
1:14
supportclaim_contract
1:25
1:25
1:25
1:30
1:30
1:30
1:42
1:43
2:01
2:02
$ls -la /pruva/project-cache/ 2>/dev/null; ls /pruva/project-cache/repo 2>/dev/null | head; ls /pruva/project-cache/repo-mirrors 2>/dev/null; cat /pruva/project-cache/.pruva/cache_manifest.json 2>/dev/null
0.4s✓
total 4
drwx------ 4 vscode vscode   80 Oct 14 14:38 .
drwxr-xr-x 3 root   root   4096 Oct 14 14:38 ..
drwx------ 2 vscode vscode   60 Oct 14 14:38 .pruva
drwx------ 2 vscode vscode   40 Oct 14 14:38 repo-mirrors
{
  "schema_version": 1,
  "entries": [
2:14
2:15
2:18
web search
2:33
2:35
2:44
2:45
$curl -sL https://curl.se/docs/CVE-2026-92392.json | head -c 3000
0.4s✓
{"cwd":"/workspace","duration_ms":178,"exit_code":0,"stderr":"","stdout":"{\n  \"schema_version\": \"1.5.0\",\n  \"id\": \"CURL-CVE-2026-92392\",\n  \"aliases\": [\n    \"CVE-2026-92392\"\n  ],\n  \"summary\": \"HTTP/2 upload Use-After-Free\",\n  \"modified\": \"2026-09-17T12:57:32.00Z\",\n  \"database_specific\": {\n    \"package\": \"curl\",\n    \"affects\": \"both\",\n    \"URL\": \"https://curl.se/docs/CVE-2026-92392.json\",\n    \"www\": \"https://curl.se/docs/CVE-2026-92392.html\",\n    \"issue\": \"https://hackerone.com/reports/4030333\",\n    \"CWE\": {\n      \"id\": \"CWE-416\",\n      \"desc\": \"Use After Free\"\n    },\n    \"last_affected\": \"8.22.0\",\n    \"severity\": \"High\"\n  },\n  \"published\": \"2026-10-14T08:00:00.00Z\",\n  \"affected\": [\n    {\n      \"ranges\": [\n        {\n           \"type\": \"SEMVER\",\n           \"events\": [\n             {\"introduced\": \"8.12.0\"},\n             {\"fixed\": \"8.14.3\"},\n             {\"introduced\": \"8.15.0\"},\n             {\"fixed\": \"8.16.2\"},\n             {\"introduced\": \"8.17.0\"},\n             {\"fixed\": \"8.20.2\"},\n             {\"introduced\": \"8.21.0\"},\n             {\"fixed\": \"8.2… [truncated]

Artifacts and Evidence for CVE-2026-92392

Scripts, logs, diffs, and output captured during the reproduction.

bundle/repro/evil_server.py30.7 KB
bundle/repro/rca_report.md11.3 KB
bundle/repro/rce-evidence-pinned/server.log22.3 KB
bundle/repro/rce-fixed-1/marker-name.txt0.0 KB
bundle/repro/rce-fixed-1/victim-cwd-ls.txt0.1 KB
bundle/repro/rce-fixed-1/victim-exit.txt0.0 KB
bundle/repro/rce-fixed-1/victim.log3.3 KB
bundle/repro/rce-fixed-2/marker-name.txt0.0 KB
bundle/repro/rce-fixed-2/observation.json0.1 KB
bundle/repro/rce-fixed-2/server.log12.6 KB
bundle/repro/rce-fixed-2/victim-cwd-ls.txt0.1 KB
bundle/repro/rce-fixed-2/victim-exit.txt0.0 KB
bundle/repro/rce-fixed-2/victim.log3.3 KB
bundle/repro/rce-vuln-1/leak-size.txt0.0 KB
bundle/repro/rce-vuln-1/marker-name.txt0.0 KB
bundle/repro/rce-vuln-1/victim-cwd-ls.txt0.1 KB
bundle/repro/rce-vuln-1/victim-exit.txt0.0 KB
bundle/repro/rce-vuln-1/victim.log0.8 KB
bundle/repro/rce-vuln-2/leak-size.txt0.0 KB
bundle/repro/rce-vuln-2/marker-name.txt0.0 KB
bundle/repro/rce-vuln-2/victim-cwd-ls.txt0.1 KB
bundle/repro/rce-vuln-2/victim-exit.txt0.0 KB
bundle/repro/rce-vuln-2/victim.log0.9 KB
bundle/repro/rce_kit/Dockerfile4.2 KB
bundle/repro/rce_kit/rce/ca/labca.crt1.1 KB
bundle/repro/rce_kit/rce/ca/server.pem4.0 KB
bundle/repro/rce_kit/rce/run_server.sh1.7 KB
bundle/repro/rce_kit/rce/sample_docs.md572.3 KB
bundle/repro/rce_kit/rce/server/h2srv_gate.py13.6 KB
bundle/repro/rce_kit/rce/server/mkstage1.py0.9 KB
bundle/repro/rce_kit/rce/server/scenario_learn.py8.5 KB
bundle/repro/rce_kit/rce/server/scenario_z2.py33.2 KB
bundle/repro/rce_kit/rce/server/stage1_z.bin3.3 KB
bundle/repro/rce_kit/rce/server/tls13_takeover.py10.2 KB
bundle/repro/rce_kit/rce/server/z_tail_g.py46.7 KB
bundle/repro/reproduction_steps.sh18.8 KB
bundle/repro/runtime_manifest.json5.6 KB
bundle/repro/validation_verdict.json1.9 KB
08 · How to Fix

How to Fix CVE-2026-92392

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-92392 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-92392

Is CVE-2026-92392 exploitable?

Yes. Pruva independently reproduced CVE-2026-92392 and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00386).

How severe is CVE-2026-92392?

CVE-2026-92392 is rated high severity.

What type of vulnerability is CVE-2026-92392?

CVE-2026-92392 is classified as CWE-416 Use After Free (Use After Free), a RCE vulnerability.

How can I reproduce CVE-2026-92392?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-92392 reproduction verified?

Yes. Pruva reproduced CVE-2026-92392 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-92392

Authoritative sources for CVE-2026-92392 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.