CVE-2026-63030: Verified Repro With Script Download
CVE-2026-63030: WordPress 7.0.1 pre-auth fresh-administrator chain to RCE
CVE-2026-63030 is verified against the affected target. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00294.
What Is CVE-2026-63030?
CVE-2026-63030 is a critical pre-authentication remote code execution chain in WordPress 7.0.1. An unauthenticated attacker composes several stock REST and post-processing behaviors to create a brand-new administrator account and then execute code, without any prior access. Pruva reproduced the full chain end-to-end (reproduction REPRO-2026-00294).
CVE-2026-63030 Severity & CVSS Score
CVE-2026-63030 is rated critical severity, with a CVSS base score of 9.8 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
How to Reproduce CVE-2026-63030
pruva-verify REPRO-2026-00294 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00294/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-63030
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
Unauthenticated nested JSON requests to stock POST /wp-json/batch/v1 plus attacker-chosen fresh administrator username/password; no owner credential is supplied to the exploit driver.
- REST batch request/handler desynchronization
- shifted posts handler
- scalar author_exclude SQL injection/full-row projection
- oEmbed cache poisoning and partial-update merge
- forged due Customizer changeset
- privileged parse_request REST re-entry
- normal users create handler
- fresh administrator
- wp-login.php
- core plugin uploader
- plugin HTTP command endpoint.
PRUVA-RCE-BEGIN
PRUVA-CMD-MARKER
uid=33(www-data) gid=33(www-data) groups=33(www-data)
PRUVA-RCE-EXIT:0 reproduction_steps.sh No distinct variant or WordPress 7.0.2 bypass was confirmed. A body-scalar alternate path failed on both targets; the query-scalar primitive reflected a synthetic row only on WordPress 7.0.1; malformed-batch alignment and exact trunk source checks show all three independent fixes remain effective.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-63030
WordPress 7.0.1 contains a composable pre-authentication chain in its stock REST and post-processing paths. A malformed REST batch entry desynchronizes parsed requests from matched handlers; a shifted posts handler then accepts an attacker-controlled scalar author_exclude value that reaches WP_Query::author__not_in without integer normalization. The resulting full-row SQL projection lets an unauthenticated attacker poison post-cache objects. Core oEmbed partial-update and hierarchy-repair behavior turns those transient objects into persistent forged posts, including a due Customizer changeset. While core applies that changeset under its recorded owner context, a forged request post with status parse invokes the stock parse_request hook and re-enters REST dispatch. A nested malformed batch consequently reaches the normal user-creation controller as the owner and creates a fresh attacker-chosen administrator. That new account can authenticate normally, use the core plugin uploader, and execute a command as the web server. WordPress 7.0.2 aligns batch arrays, integer-normalizes the ID list, and blocks nested top-level REST dispatch.
- Affected component: WordPress Core REST batch dispatch,
WP_Query, post/oEmbed cache update behavior, Customizer changeset publication, and nested REST dispatch. - Affected version exercised: stock WordPress 7.0.1, official
wordpress:7.0.1-php8.2-apacheimage pinned tosha256:bfc320ed4f02dd3939186b8020de64203a48a939d6dedcf44cb92cf2368923f5. - Fixed control exercised: WordPress 7.0.2 source at commit
855551c4477bd5a0407221c57dae123c4163b434, over the same pinned base image. - Database: MySQL 8.0 pinned to
sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b, with@@local_infile=0, application-scoped grants, and no globalFILEprivilege. - Risk: Critical. An unauthenticated network attacker can create a new administrator without knowing, recovering, or reusing the sole owner's random password, then obtain arbitrary command execution as Apache's
www-dataidentity.
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated remote code execution through creation of a fresh attacker-chosen administrator.
- Reproduced impact from this run: Full unauthenticated fresh-administrator creation followed by normal login, core plugin upload, and target-originated command execution as
uid=33(www-data) gid=33(www-data) groups=33(www-data). The final generated script passed twice consecutively; each execution itself recreated and passed two clean vulnerable/fixed matrices. - Parity:
full. - Not demonstrated: None of the claimed chain remains unproved.
Root Cause
Three missing invariants in WordPress 7.0.1 compose into the privilege transition:
- REST batch request/handler array desynchronization.
WP_REST_Server::serve_batch_request_v1()appends a parse error to its request array but, in the vulnerable source, does not append a corresponding entry to the matched-handler array before continuing. Subsequent requests are paired with the next route's handler and permission callback. - Scalar
author__not_inSQL injection. A shifted posts-list handler retains attacker-controlled query parameters. In 7.0.1,author__not_inis normalized only when already represented as an array; a scalar can reach the interpolatedNOT IN (...)SQL clause. A high page size andorderby=nonedisable the ID-only split-query boundary, allowing a complete 23-column forgedwp_posts.*row. - Nested top-level REST dispatch. Core's dynamic
{$new_status}_{$post_type}transition permits the forgedparse_requestpair to callrest_api_loaded()while an outer REST dispatch is active. The vulnerable implementation re-entersserve_request()during the Customizer's temporary owner context. The nested malformed batch then invokesWP_REST_Users_Controller::create_item()with permission checks evaluated as user 1.
The middle state transition uses stock behavior rather than a plugin gadget: an ID-zero embed triggers WP_Embed::shortcode(), which performs a partial wp_update_post() on a real cache row. wp_update_post() first loads and merges all fields from the poisoned cached object; hierarchy repair performs another partial update and persists the forged fields. A due customize_changeset then applies stock custom CSS under the recorded owner context, and the forged status/type transition selects the existing parse_request action.
The fixed release adds batch-array alignment (c8bdf1fa12355f79db94054d307d0e3898b501c9), parses author exclusion values through an integer-only ID list, and guards against nested REST dispatch. The fixed source identity used by the control is commit 855551c4477bd5a0407221c57dae123c4163b434 (WordPress 7.0.2).
Reproduction Steps
- Run
bundle/repro/reproduction_steps.shfrom any working directory (or setPRUVA_ROOTto the bundle directory). - The script verifies the ticket-pinned image digests and exact 7.0.2 source archive, starts isolated MySQL and WordPress targets, and installs each with exactly one owner whose random password is generated inside the installer and is neither printed nor supplied to the exploit driver.
- It confirms no exploit plugin or mu-plugin is present, application DB grants are scoped to
wordpress.*, and@@local_infile=0. - A distinct Python driver container sends unauthenticated HTTP requests through
/wp-json/batch/v1, seeds and rediscovers disposable oEmbed rows through the vulnerable application path, composes the forged changeset/request state, and creates a randomized administrator. - The driver authenticates through
wp-login.php, uploads a randomized proof plugin through the core uploader, and requests it over HTTP to executeprintf 'PRUVA-CMD-MARKER\n'; id. - The exact same trigger is run against WordPress 7.0.2 and must fail before its first disposable oEmbed cache row can be discovered. The script verifies one user and zero
oembed_cacherows afterward. - The script destroys both target stacks, recreates clean databases and targets, and repeats the complete vulnerable/fixed matrix.
Expected success output ends with:
[+] CONFIRMED: WordPress 7.0.1 pre-auth fresh-administrator chain to command execution
[+] Two clean vulnerable/fixed matrices passed
Evidence
The current script records fresh evidence under bundle/logs/:
reproduction_steps.log: complete build, setup, trigger, oracle, and cleanup transcript.source_identity.log: fixed commit, ticket source identities, bundled 7.0.2 archive checksum, and stable prepared repository path.image_identity.log: requested image digests and resolved Docker image IDs.network_boundary.log: distinct driver/provider and target identities plus isolated network state.vulnerable_setup.log: target version, image identity, mounts, stock plugin inventory, absent mu-plugins, initial user/oEmbed counts, grants, andlocal_infilestate.vulnerable_database_before.logandvulnerable_database_after.log: one-to-two user transition, fresh username/ID, administrator metadata, and oEmbed state.vulnerable_http_chain.log: full remote-chain JSON including generated administrator identity, normal-login result, plugin-upload result, and command response.vulnerable_command_response.log: target-originated command markers and exactwww-dataidentity.fixed_setup.log,fixed_http_chain.log, andfixed_database_after.log: unchanged-trigger failure before cache discovery and fixed postconditions.second_*logs andsecond_clean_run.log: independent clean-target repeat.bundle/repro/runtime_manifest.json: strict runtime evidence manifest written on every attempt.
Fresh current-run excerpts from the final two consecutive script executions include:
administrator.id=2
administrator.roles=["administrator"]
normal_login_confirmed=true
plugin_upload_confirmed=true
PRUVA-RCE-BEGIN
PRUVA-CMD-MARKER
uid=33(www-data) gid=33(www-data) groups=33(www-data)
PRUVA-RCE-EXIT:0
The matching database evidence records user_count=2, admin=2:<generated username>, while each fixed control records user_count=1 and oembed_count=0. The unchanged fixed trigger raises oEmbed seed did not create a discoverable cache row, proving first divergence before cache discovery. logs/pass1/ preserves the preceding successful top-level execution; the root logs/ files are the second successful execution. Historical proof-carry data is not treated as evidence for this verdict.
Recommendations / Next Steps
- Upgrade affected installations to WordPress 7.0.2 or later.
- Preserve the batch parser invariant by appending exactly one matched-handler and validation entry for every parsed sub-request, including parse errors.
- Normalize every author include/exclude input with
wp_parse_id_list()before SQL construction, regardless of the original scalar/array shape. - Refuse top-level
serve_request()re-entry while the REST server is already dispatching; guard bothrest_api_loaded()and the server entry point. - Add regression coverage for malformed entries at every batch position, scalar/list query-parameter forms, nested batch bodies, and dynamic status/type hooks reached during privileged context windows.
- Add end-to-end tests asserting that the unchanged 7.0.1 trigger cannot create oEmbed cache state, users, or plugin files on fixed releases.
Additional Notes
- The final proof uses the real stock HTTP/REST, login, and plugin-upload boundaries; no sanitizer, direct parser harness, or source-only oracle is used.
- The exploit driver performs no direct database write and receives no owner password or database credential.
- The oEmbed service is an isolated provider that returns valid bounded iframe HTML; it does not fabricate the command marker or privileged state.
- The vulnerable target uses no modified WordPress tree. The fixed control intentionally overlays the complete exact 7.0.2 source onto the same pinned official 7.0.1 PHP/Apache base.
- Container names are randomized and cleanup is trapped, making the script safe to repeat. It also recreates both vulnerable and fixed targets internally for a second clean matrix.
- Idempotency is confirmed: the final script was executed twice consecutively from clean targets, both exits were zero, generated administrator usernames differed, and every vulnerable/fixed oracle passed. Each script execution also performs an internal clean teardown/recreation, yielding four fresh vulnerable successes and four fixed controls across the two required top-level runs.
Variant Analysis & Alternative Triggers for CVE-2026-63030
No distinct vulnerability variant or WordPress 7.0.2 bypass was found. The bounded search exercised the original unauthenticated /wp-json/batch/v1 trust boundary against clean stock WordPress 7.0.1 and exact 7.0.2 targets, tested a materially different data path that moved the shifted scalar author_exclude value from the URL query string into the subrequest JSON body, retained the original query-scalar path as a live primitive control, and separately examined malformed-entry placement in batch dispatch. The query-scalar control reflected a forged full-row marker on 7.0.1 and not on 7.0.2; the body-scalar candidate reflected no forged marker on either target. Exact trunk revision ace9192af868524bdc49cf4fcb91f4c12c73ee5f retained all three independent guards. The result is therefore negative rather than a fabricated alternate label for the parent chain.
Fix Coverage / Assumptions
The 7.0.2 fix establishes three independent invariants:
WP_REST_Server::serve_batch_request_v1()appends aWP_Errorto$matcheswhenever it appends the same error to$validation, preserving one-to-one request/match/validation indexing for every malformed subrequest (src/wp-includes/rest-api/class-wp-rest-server.php).WP_Query::get_posts()parses every non-emptyauthor__not_invalue withwp_parse_id_list()before interpolating the resulting integer list into SQL (src/wp-includes/class-wp-query.php). This is shape-independent at the sink: scalar strings and arrays are normalized.rest_api_loaded()andWP_REST_Server::serve_request()refuse to start a fresh top-level REST cycle while a dispatch is active (src/wp-includes/rest-api.phpandsrc/wp-includes/rest-api/class-wp-rest-server.php). Internal subrequests must usedispatch().
These changes assume that every batch parse failure is represented by a WP_Error, all author-exclusion flows reaching WP_Query use author__not_in, and privileged re-entry requires a fresh serve_request() cycle. Source scanning supports those assumptions for the parent chain. At the exact tested trunk revision, the sole production call to serve_request() remained in rest_api_loaded(), which is itself guarded, while the server method also retains a defense-in-depth guard.
The patch does not redesign the oEmbed-cache merge behavior, Customizer per-setting user switching, dynamic post-status/type hooks, or normal REST dispatch(). Those paths remain legitimate core behaviors. They are no longer attacker-reachable through the parent chain because the patch cuts the chain at three earlier, independent gates. The only asymmetry noticed in nearby code is that author__in retains a separate normalization implementation, but it applies absint to every value and does not preserve the parent SQL-injection sink; relabeling it as a bypass would be unsupported.
Variant / Alternate Trigger
All runtime candidates used unauthenticated HTTP POST /wp-json/batch/v1 from a separate driver container:
- Body-scalar alternate data path. The shifted request placed
author_excludein its JSON body instead of the URL query string, while keeping the malformed nested batch and full-row marker query. This was materially different input placement but did not reach the SQL marker on either 7.0.1 or 7.0.2. On 7.0.1 the shifted GET posts handler executed but did not consume this body value as the needed query parameter. - Query-scalar sink control. The scalar remained in the shifted subrequest URL query. A synthetic
wp_posts.*row containingPRUVA-VARIANT-ROW-MARKERwas reflected on 7.0.1, proving the vulnerable batch-to-WP_Querypath was live in this stage's fresh environment. The marker was absent on 7.0.2, where aligned handlers stop the shifted route and integer normalization independently removes SQL syntax. - Alternate malformed-entry/alignment check. A malformed subrequest was followed by post and user routes to compare handler selection. On 7.0.1 the response showed the shifted user-controller permission code (
rest_cannot_create_user) for the posts request; 7.0.2 showed the correctly aligned posts-controller code (rest_cannot_create). This confirms the alignment patch rather than a transport failure. Moving the malformed error later cannot shift an earlier request and leaves no subsequent attacker request to exploit when last; multiple errors are covered by appending one match entry per error.
The relevant path is WP_REST_Server::serve_batch_request_v1() -> shifted WP_REST_Posts_Controller::get_items() -> WP_Query::get_posts() for the primitive, followed in the parent chain by oEmbed/cache persistence, Customizer publication, rest_api_loaded()/serve_request() re-entry, and WP_REST_Users_Controller::create_item(). No alternate stock endpoint was found that both crosses the same unauthenticated network boundary and bypasses all three fixed invariants.
- Component: WordPress Core REST batch dispatch and
WP_Queryauthor exclusion handling. - Vulnerable control tested: stock WordPress 7.0.1 source identity
18f793b1f16c1b15b0fc37027f4aeaefab0bfe02. - Fixed target tested: WordPress 7.0.2 tag/commit
855551c4477bd5a0407221c57dae123c4163b434over the ticket-pinned PHP/Apache image. - Latest checked: origin/trunk at exact revision
ace9192af868524bdc49cf4fcb91f4c12c73ee5fon 2026-07-18; all three guards were present. Latest was source-inspected rather than started as a third runtime because it is an unreleased 7.1 development tree and the exact released 7.0.2 fixed target supplied the runtime oracle. - Risk of the parent issue: Critical unauthenticated administrator creation leading to code execution.
- Risk established by this variant run: None on the fixed target. No persistent write, user creation, privilege transition, or code execution occurred.
Impact Parity
- Disclosed/claimed maximum parent impact: Unauthenticated creation of a fresh administrator followed by normal login, core plugin upload, and command execution as
www-data. - Reproduced impact in this variant run: A side-effect-free full-row SQL reflection marker only on the vulnerable 7.0.1 control. No marker reached 7.0.2; both clean targets retained one user and their original post counts.
- Parity:
nonefor a fixed-version bypass. The vulnerable-control primitive is partial evidence only and is not claimed as a new variant. - Not demonstrated: Fixed-version SQL injection, cache poisoning, persistent post creation, administrator creation, authentication, plugin upload, or code execution.
Root Cause
There is no surviving same-root-cause path on the tested fixed target. The parent root cause requires composition of (a) batch-array desynchronization, (b) unnormalized scalar author__not_in SQL construction, and (c) nested top-level REST dispatch during a temporary privileged context. WordPress 7.0.2 fixes each condition independently in commits merged into tag 855551c4477bd5a0407221c57dae123c4163b434:
85015b84fbc52bf6151a691299896b8971772594— nested top-level REST dispatch guards.c8bdf1fa12355f79db94054d307d0e3898b501c9— batch match-array alignment.74d37a344cbf28e9187a1a5ca71b33d186bcd333— integer normalization ofauthor__not_in.
The body-scalar experiment did not reproduce even on 7.0.1 because request-body placement did not become the posts collection's query parameter in this shifted GET-handler path. The exact query-scalar parent primitive did reproduce on 7.0.1, then failed on 7.0.2 as expected. Consequently there is no technical basis to claim that the underlying bug remains reachable.
Reproduction Steps
- Run
bundle/vuln_variant/reproduction_steps.shfrom any directory, optionally settingPRUVA_ROOTto the bundle directory. - The script resolves and records exact 7.0.1, 7.0.2, and trunk source identities; reads the target security policy; verifies the three guards in 7.0.2 and trunk; launches clean isolated MySQL and WordPress 7.0.1/7.0.2 targets; installs each with one owner whose random password is never exposed; and runs all three candidates over unauthenticated HTTP.
- It requires the vulnerable query-scalar marker to be present, the fixed marker to be absent, and user/post counts to remain unchanged. It exits
1for this valid negative outcome. Exit0is reserved for a marker reaching the fixed target, and exit2indicates an infrastructure/oracle failure. - Expected final evidence is
verdict=no_distinct_variant_or_bypassandNO DISTINCT VARIANT/BYPASS.
Evidence
Primary logs are under bundle/logs/vuln_variant/:
verification_run1.logandverification_run2.log: two independent complete script executions.two_run_verification.log: both expected exits were1.vulnerable_query_scalar.json:marker_reflected=true, proving the vulnerable primitive was live.fixed_query_scalar.json:marker_reflected=false.vulnerable_body_scalar.jsonandfixed_body_scalar.json: alternate body path was negative on both.vulnerable_batch_alignment.jsonandfixed_batch_alignment.json: shifted versus aligned controller response codes.vulnerable_before.log,vulnerable_after.log,fixed_before.log, andfixed_after.log: both targets stayed atuser_count=1,post_count=4,oembed_count=0, andlocal_infile=0.source_identity.log,fixed_version.txt, andlatest_version.txt: exact commit identities.static_coverage.log: 7.0.2 and trunk each contain the batch, nested-dispatch, and author-ID guards; trunk has one productionserve_request()call site.fix_diff.txtandsecurity_policy.md: patch and target policy evidence.candidate_matrix.log: canonical bounded-search outcome.
Key evidence excerpts:
candidate_1=body_scalar_alternate_data_path vulnerable_marker=false fixed_marker=false
candidate_2=query_scalar_original_sink_control vulnerable_marker=true fixed_marker=false
candidate_3=batch_alignment_alternate_error_position inspected_runtime_and_source=true
latest_revision=ace9192af868524bdc49cf4fcb91f4c12c73ee5f guards_retained=true
verdict=no_distinct_variant_or_bypass
run1_exit=1
run2_exit=1
Recommendations / Next Steps
- Keep all three fixes; none should be treated as redundant. Add regression tests that compose malformed batch entries at first, middle, last, and multiple positions and assert exact request/match/validation alignment.
- Add
WP_Querytests covering scalar strings, arrays, comma-separated strings, nested arrays, and values supplied through query and body parameter bags, requiring integer-onlyauthor__not_inSQL. - Retain both nested-dispatch guards. Add a regression assertion that all internal REST subrequests use
dispatch()and thatserve_request()cannot re-enter whileis_dispatching()is true. - Consider normalizing
author__inthroughwp_parse_id_list()too for consistency and stable cache-key behavior, although currentabsintmapping did not expose the parent sink. - Add an end-to-end test that the parent unauthenticated request leaves users, posts, oEmbed rows, and plugin state unchanged on supported releases.
Additional Notes
- WordPress's repository
SECURITY.mdexplicitly covers Core and directs reports to HackerOne. It does not exclude unauthenticated REST privilege transitions; the tested boundary is therefore in scope. The probes do not reframe a user-selected local file or documented behavior as a security issue. - Runtime probes were intentionally side-effect-free: the SQL candidate only reflected a synthetic row in the REST response and did not persist it. Database counts verify this.
- Idempotency was confirmed by two consecutive complete script runs, each ending with the expected exit code
1and the same negative matrix. - The latest default branch was exact-revision source checked, not runtime built, because it was an unreleased 7.1 development revision. The fixed 7.0.2 release was tested live side by side as required.
CVE-2026-63030 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired. Phases: support · claim contract · reproduction · judge · variant analysis
Full session Replay every step — scrub the timeline or play it back.
docker ps --format '{{.ID}} {{.Image}} {{.Names}}' && docker version --format '{{.Server.Version}}'{"cwd":"/data/pruva/runs/abec77ca-14dc-4055-8ebb-ac80871618ee/bundle","duration_ms":106,"exit_code":0,"stderr":"","stdout":"a67e10b66441 wordpress:7.0.1-php8.2-apache pruva-wp2shell-pure701-wp\n4da94e135114 mysql:8.0 pruva-wp2shell-pure701-db\n7ad432ff37c7 wordpress:7.0.1-php8.2-apache pruva-wp2shell-clean702-wp\n9e5b16b7816c mysql:8.0 pruva-wp2shell-clean702-db\n580eee2cd9e6 wordpress:7.0.1-php8.2-apache pruva-wp2shell-clean701-wp\nde3c483d5ea2 mysql:8.0 pruva-wp2shell-clean701-db\ncfb483a5f5c7 wordpress:7.0.1-php8.2-apache pruva-wp2shell-rce-wp\n37f17b82740d mysql:8.0 pruva-wp2shell-rce-db\ncb7f7fc2eea0 athena-pruva-worker-sandbox:latest pruva-wp2shell-rce-lab\n0195d2fe98c5 pruva-cve-2026-42533-slice-vuln:local spray-one\nb8e9f07d4649 athena-pruva-worker-sandbox:latest athena-pruva-worker-glm52-gpt55\n6fe65842cc97 athena-pruva-worker-sandbox:latest athena-pruva-worker-kimi27-gpt55\n538e7090ed37 athena-pruva-worker-sandbox:latest athena-pruva-worker-glm52-gpt55-repro2\n970810b9df5d athena-pruva-worker-sandbox:latest athena-pruva-worker-repro-unicorn-gpt56-max\n8a584f46c1dc athena-pruva-worker-sandbox:latest athena-pruva-worker-unicorn-gpt55-xhigh-1\n71d1eb1b3c4c athena-pruva-worke… [truncated]Artifacts and Evidence for CVE-2026-63030
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-63030
FAQ: CVE-2026-63030
Is CVE-2026-63030 exploitable without authentication?
How does the CVE-2026-63030 exploit chain work?
How severe is CVE-2026-63030?
How can I reproduce CVE-2026-63030?
References for CVE-2026-63030
Authoritative sources for CVE-2026-63030 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.