
Python-PoC für CVE-2026-94504, ein unauthentifizierter gespeicherter XSS in Ninja Forms <= 3.15.3. Fingerprintet anfällige Versionen und platziert eine Payload über nf_ajax_submit.
Ninja Forms unauth stored XSS POC
Ninja Forms ≤ 3.15.3 — unauthentifizierte gespeicherte XSS. Ein öffentliches Nicht-RTE-Textarea wird gespeichert, dann html_entity_decode()'d und ohne esc_textarea() in den Legacy-Submission-Editor geschrieben. 3.15.4 escaped es. Es ist kein Login erforderlich, um die Payload zu platzieren. Sie wird ausgeführt, wenn ein Editor diese Submission öffnet.
Nur für autorisierte Tests. Führe dies nicht gegen Systeme aus, die dir nicht gehören oder für die du keine Testberechtigung hast.
Erfordert: Python 3, nur Standardbibliothek.
python3 cve-2026-94504-ninja-forms-stored-xss.py -u http://TARGET --test
python3 cve-2026-94504-ninja-forms-stored-xss.py -u http://TARGET --plant
python3 cve-2026-94504-ninja-forms-stored-xss.py -u https://TARGET --test --insecure
python3 cve-2026-94504-ninja-forms-stored-xss.py -u TARGET --test
| Flag | Was es tut |
|---|
-u / --url | WordPress-Basis-URL (erforderlich). http:// oder https://. Ein Hostname ohne Schema wird zuerst als HTTPS versucht, dann als HTTP. http→https-Weiterleitungen behalten POST bei. Ungültige TLS-Zertifikate werden ohne Verifizierung erneut versucht; --insecure überspringt die Verifizierung von Anfang an |
--test | Nur Fingerprinting. Liest readme.txt, findet ein öffentliches Nicht-RTE-Textarea. Sendet nichts |
--plant | Unauthentifiziertes nf_ajax_submit. Füllt erforderliche Felder aus, platziert die Payload im Textarea und gibt die Bearbeitungs-URL aus. Öffnet keinen Browser |
--page | Seite, die das Formular einbettet. Standardmäßig werden /, /contact/ und die REST-Seiten-/Beitragsliste gecrawlt |
--field | Textarea-Feld-ID. Standard ist das erste Nicht-RTE-Textarea |
--payload | Textarea-Wert. Standard schließt das Admin-<textarea> mit einem img onerror. In cmd.exe dies nicht in einfache Anführungszeichen setzen (< ist eine Weiterleitung). --payload weglassen oder doppelte Anführungszeichen und einfache Anführungszeichen innerhalb von alert verwenden: --payload "</textarea>" |
--name / --email | Füller für andere erforderliche Felder (Standard Ada Lovelace / [email protected]) |
--insecure | TLS-Verifizierung überspringen |
--timeout | HTTP-Timeout in Sekunden (Standard 30) |
--test liest /wp-content/plugins/ninja-forms/readme.txt (Stable tag) und eine öffentliche Seite, deren nfForms-JSON "type":"textarea" mit deaktiviertem Rich-Text-Editor enthält. ≤ 3.15.3 plus dieses Feld ist VULNERABLE. ≥ 3.15.4 ist NOT_VULNERABLE. Kein Formular auf der Seite ist NEED_FORM.
--plant sendet /wp-admin/admin-ajax.php action=nf_ajax_submit mit dem ajaxNonce der Seite. Kein WordPress-Konto. Bei Erfolg werden PAYLOAD, SUB und EDIT /wp-admin/post.php?post=ID&action=edit ausgegeben. Das Skript wird ausgeführt, wenn diese Submission in wp-admin geöffnet wird. Die öffentliche Formularseite nicht.
Rich-Text- (textarea_rte) Absätze sind ein anderer Pfad und werden übersprungen. Das übliche Ziel ist das Standard-„Message"-Feld von Contact Me.
Stdout: VERSION, PAGE, FORM, FIELD, PRESENT, VULNERABLE, NOT_VULNERABLE, NOT_FOUND, NEED_FORM, NEED_NONCE, PLANTED, PAYLOAD, SUB, EDIT.
CVE: CVE-2026-94504. Plugin: ninja-forms. Gepatcht in 3.15.4. Danksagung: Hippolyte Quéré (Hippie); das 3.15.4-Changelog nennt außerdem Venkateswara Reddy Challa für den Admin-Escaping-Fix.

