# REPRO-2026-00263: c-ares CVE-2026-33630 RCE primitive construction from confirmed remote UAF ## Summary Status: published Severity: high Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00263 CVE: CVE-2026-33630 ## Package Name: c-ares/c-ares Ecosystem: c Affected: >1.32.3 through 1.34.6 Fixed: 1.34.7 ## Root Cause ## Summary CVE-2026-33630 in c-ares is a remotely reachable use-after-free/double-free in query-completion handling that can be triggered through the public `ares_getaddrinfo()` API over TCP DNS. In c-ares v1.34.6, deferred ENDQUERY processing invokes a query callback while the query remains linked in the channel lookup tables. A malicious TCP DNS peer can drive a reentrant completion path that frees the associated `host_query`; subsequent stale use can consume reclaimed attacker-shaped fields. This run demonstrates a vulnerable-only benign control-flow hijack marker: under the real TCP DNS trigger and a deterministic public allocator-hook heap reclaim, v1.34.6 executes a sentinel callback from a forged reclaimed `host_query`, while v1.34.7 never executes the marker. ## Impact - Package/component affected: c-ares, specifically `src/lib/ares_process.c` deferred requeue/endquery handling and `src/lib/ares_getaddrinfo.c` `host_query` callback completion state. - Affected versions: reproduced on c-ares v1.34.6 (`3ac47ee46edd8ea40370222f91613fc16c434853`). - Fixed version/control: c-ares v1.34.7 (`d63b7de3a3568d14ab1351374b205365aed51cca`), which contains fix commit `d823199b688052dcdc1646f2ab4cb8c16b1c644a`. - Risk level and consequences: high. A malicious DNS/TCP peer can drive the vulnerable completion timing through a real resolver API. This run demonstrates that reclaimed `host_query` callback fields can be consumed as control flow in the vulnerable build, which is equivalent to a code-execution primitive in-process when heap content is controlled. ## Impact Parity - Disclosed/claimed maximum impact: code execution/RCE primitive construction from the confirmed remote c-ares UAF/double-free. - Reproduced impact from this run: vulnerable-only sentinel function execution (`SENTINEL EXECUTION MARKER FIRED`) through a forged reclaimed `host_query` object after a real malicious TCP DNS peer drives `ares_getaddrinfo()`. - Parity: `full`. - Not demonstrated: no shell command execution or external payload execution was attempted. The proof uses a benign in-process sentinel callback as the code-execution/control-flow marker, which the ticket explicitly allowed as an acceptable safe proof form. ## Root Cause In v1.34.6, `read_answers()` accumulates deferred ENDQUERY entries in a requeue array and later flushes them by looking up the query by QID, invoking `query->callback(query->arg, ...)`, and only then freeing/detaching the query with `ares_free_query(query)`. Because the query remains visible in the channel's `queries_by_qid`/`all_queries` structures while its callback is running, callback-driven reentrant resolver activity can find and complete/free related state before the outer completion path has finished. For the `ares_getaddrinfo()` path, `host_callback()` may call `end_hquery()`/`hquery_free()` during this reentrant sequence, then later stale code continues to operate on the freed `host_query` pointer. The fixed code introduces centralized `ares_flush_requeue()` handling and detaches the query before invoking the callback. In the fixed implementation, the ENDQUERY drain performs `ares_detach_query(query)` before `query->callback(...)`, so reentrant cancellation/completion cannot rediscover and free the same query or drive the stale `host_query` consumption window. The reproduction anchors the negative control to v1.34.7, which contains fix commit `d823199b688052dcdc1646f2ab4cb8c16b1c644a`. ## Reproduction Steps 1. Use `bundle/repro/reproduction_steps.sh`. 2. The script reuses or clones c-ares into the prepared project cache, checks out v1.34.6 and v1.34.7, verifies that only the fixed checkout contains `ares_flush_requeue`, builds static non-sanitized `libcares.a` for both, and compiles `bundle/repro/rce_reclaim_harness.c` against each build. 3. The harness calls public `ares_getaddrinfo(AF_INET)` and sends traffic through a real localhost TCP DNS socket to a malicious peer. The peer sends FORMERR without OPT, a duplicate same-QID NXDOMAIN, observes the follow-on search-domain DNS query, and resets the TCP connection. 4. The harness uses the public `ares_library_init_mem()` allocator hooks to deterministically retain and rewrite freed 144-byte `host_query` chunks with a forged callback/arg/addrinfo layout. Success is counted only when c-ares later consumes the forged callback and fires the sentinel. 5. The script runs 30 vulnerable attempts and 10 fixed attempts. Expected evidence is at least two vulnerable sentinel executions and zero fixed sentinel executions, with TCP DNS boundary evidence in both builds. ## Evidence Key artifacts: - `bundle/repro/reproduction_steps.sh` — self-contained reproducer. - `bundle/repro/rce_reclaim_harness.c` — C harness using real c-ares public APIs and real TCP DNS peer. - `bundle/logs/reproduction_steps.log` — aggregate build/run/proof log. - `bundle/logs/rce_reclaim_vuln_attempt*.log` — vulnerable attempt logs. - `bundle/logs/rce_reclaim_fixed_attempt*.log` — fixed negative-control logs. - `bundle/repro/runtime_manifest.json` — runtime evidence manifest. - `bundle/repro/validation_verdict.json` — structured verdict. The latest verified run ended with: ```text [repro] counts: vuln_boundary_count=30 fixed_boundary_count=10 vuln_forge_count=30 rce_marker_count=4 fixed_marker_count=0 [repro] wrote validation_verdict.json: claim_outcome=confirmed observed_impact_class=code_execution [repro] proof summary: rce_marker_fired=1 rce_marker_count=4 fixed_marker=0 fixed_marker_count=0 vuln_boundary=1 fixed_boundary=1 controlled_reclaim=1 vuln_runs_ok=1 fixed_runs_ok=1 [repro] SUCCESS: vulnerable-only sentinel code execution marker fired through reclaimed host_query over real TCP DNS path ``` Representative vulnerable marker evidence: ```text [server] accepted TCP DNS connection on 127.0.0.1:40863 [server] sending FORMERR without OPT for qid=22298 [server] sending second response with duplicate qid=22298 (NXDOMAIN, no OPT) [server] observed follow-on search-domain query qid=22298 qtype=1 length=33 [server] resetting TCP connection with SO_LINGER zero [allocator] FORGED_RECLAIM host_query ptr=0x5588b747d020 callback@24=0x55888b4cbcf9 arg@32=0x7ffeddcb4bc8 ai@112=0x5588b7490e30 remaining@128=1 [RCE] *** SENTINEL EXECUTION MARKER FIRED *** arg=0x7ffeddcb4bc8 status=11 timeouts=0 callback=0x55888b4cbcf9 [result] done=1 status=11 accepted=1 follow_on_seen=1 normal_callbacks=1 forged_host_query=1 forged_callback_consumed=1 rce_marker_fired=1 ``` Representative fixed negative-control evidence shows the same DNS boundary and allocator-forge opportunity but no stale consumption: ```text [server] accepted TCP DNS connection on 127.0.0.1:42041 [server] sending FORMERR without OPT for qid=24413 [server] sending second response with duplicate qid=24413 (NXDOMAIN, no OPT) [server] observed follow-on search-domain query qid=24413 qtype=1 length=33 [server] resetting TCP connection with SO_LINGER zero [allocator] FORGED_RECLAIM host_query ptr=0x55d50d23f020 callback@24=0x55d4d3286cb9 arg@32=0x7fff28da4f38 ai@112=0x55d50d2536f0 remaining@128=1 [result] done=1 status=11 accepted=1 follow_on_seen=1 normal_callbacks=1 forged_host_query=1 forged_callback_consumed=0 rce_marker_fired=0 ``` Environment and source identity captured by the script: ```text vulnerable checkout v1.34.6 -> 3ac47ee46edd8ea40370222f91613fc16c434853 fixed checkout v1.34.7 -> d63b7de3a3568d14ab1351374b205365aed51cca vulnerable ares_flush_requeue count: 0 fixed ares_flush_requeue count: 5 ``` ## Recommendations / Next Steps - Upgrade c-ares deployments to v1.34.7 or later. - Preserve the fix pattern: detach queries from all lookup structures before invoking user callbacks, and centralize deferred requeue/endquery draining so callback reentrancy cannot rediscover pending query objects. - Add regression tests that exercise malicious TCP DNS peers, duplicate-QID/deferred ENDQUERY flows, TCP reset after follow-on query, and callback reentrancy. - Add allocator-assisted and sanitizer-backed tests that verify no stale `host_query` or `ares_query` fields are consumed after callback-driven reentrant cleanup. ## Additional Notes - Idempotency confirmation: `bundle/repro/reproduction_steps.sh` was run successfully twice consecutively. The first successful full run observed 7 vulnerable marker firings out of 30 and 0 fixed marker firings out of 10. The second successful full run observed 4 vulnerable marker firings out of 30 and 0 fixed marker firings out of 10. - The vulnerable marker is probabilistic because it depends on the exact reentrant stale-use timing, so the script runs multiple bounded attempts and requires at least two vulnerable marker firings while ensuring the fixed build has zero marker firings. - The proof intentionally uses a benign in-process sentinel callback instead of executing an external command. No sanitizer is used for the primary code-execution proof. ## Reproduction Details Reproduced: 2026-07-07T19:41:35.121Z Duration: 3267 seconds Tool calls: 252 Turns: Unknown Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00263 pruva-verify CVE-2026-33630 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00263&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00263/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33630 - Source: https://github.com/c-ares/c-ares/security/advisories/GHSA-6wfj-rwm7-3542 ## Artifacts - bundle/repro/rca_report.md (analysis, 8946 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 28233 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 12636 bytes) - bundle/vuln_variant/rca_report.md (analysis, 9827 bytes) - bundle/artifact_promotion_manifest.json (other, 10546 bytes) - bundle/artifact_promotion_report.json (other, 10564 bytes) - bundle/vuln_variant/source_identity.json (other, 1038 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 1586 bytes) - bundle/logs/reproduction_steps.log (log, 29461 bytes) - bundle/repro/rce_reclaim_harness.c (other, 13508 bytes) - bundle/repro/validation_verdict.json (other, 1058 bytes) - bundle/repro/runtime_manifest.json (other, 1601 bytes) - bundle/logs/rce_reclaim_vuln_attempt1.log (log, 1355 bytes) - bundle/logs/rce_reclaim_vuln_attempt2.log (log, 1244 bytes) - bundle/logs/rce_reclaim_fixed_attempt1.log (log, 1252 bytes) - bundle/logs/rce_reclaim_fixed_attempt2.log (log, 1252 bytes) - bundle/vuln_variant/timeout_cancel_probe.c (other, 7906 bytes) - bundle/logs/vuln_variant_reproduction_steps.log (log, 4341 bytes) - bundle/logs/vuln_variant_timeout_vuln.log (log, 605 bytes) - bundle/logs/vuln_variant_timeout_fixed.log (log, 569 bytes) - bundle/vuln_variant/variant_manifest.json (other, 3921 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 6567 bytes) - bundle/vuln_variant/validation_verdict.json (other, 2434 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 1426 bytes) - bundle/logs/vuln_variant_fixed_version.txt (other, 201 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00263 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00263/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00263 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev