
XSS riflesso non autenticato in WPForms <= 2.0.2.1 — bypass dell'escaping dello Smart Tag di CVE-2026-88996 nella posizione del nome dell'attributo
Interessato: WPForms – AI Form Builder for WordPress (free/lite), versione 2.0.2.1 — la versione più recente al momento della divulgazione (nonché la versione che contiene la correzione per CVE-2026-88996). L'edizione Pro condivide il renderer dei Smart Tag.
Classe: cross-site scripting riflesso non autenticato (CWE-79) con esecuzione JavaScript verificata, tramite correzione incompleta di CVE-2026-88996 / GHSA-8j5g-9vmv-9mmw.
Scoperto: 2026-09-27 ·
Segnalato a Patchstack: 2026-09-27 ·
Rifiutato: 2026-09-28 ("il problema si manifesta solo quando l'amministratore del sito ha scritto un Smart Tag nudo dove dovrebbe esserci il nome di un attributo HTML... non è una configurazione predefinita né un pattern che qualcuno scriverebbe realisticamente" — validità tecnica non contestata) ·
Pubblico: 2026-09-28.
La correzione 2.0.2.1 per l'XSS riflesso della famiglia CVE-2026-93485 esegue l'escape dei valori dei Smart Tag che vengono
sostituiti nel markup (SmartTags::escape_for_attribute() codifica virgolette e
spazi bianchi). Ciò è efficace ogni volta che il valore sostituito finisce all'interno di un valore di
attributo tra virgolette. Quando l'autore di un modulo inserisce un Smart Tag nudo in una posizione
di nome di attributo nel messaggio di Conferma — ad esempio:
<input {field_id="3"}>
un valore di campo inviato che non contiene virgolette né spazi bianchi, come
onmouseover=alert(1), viene inserito alla lettera e diventa un nuovo attributo
event-handler attivo. L'escaping non codifica mai =, e i nomi degli attributi non richiedono né virgolette né
spazi bianchi. La conferma restituita al mittente anonimo contiene quindi:
<input onmouseover=alert(1)>
Le sottomissioni anonime a admin-ajax.php (action=wpforms_submit,
wp_ajax_nopriv_*) sono prive di nonce per progettazione per gli utenti non autenticati, quindi si tratta di un XSS riflesso completamente
non autenticato. JavaScript arbitrario viene eseguito entro il vincolo del singolo token
tramite onmouseover=eval(String.fromCharCode(...)).
Sullo stesso form, la forma del payload della CVE padre — rottura delle virgolette dall'interno di un valore tra virgolette,
ad es. x" onfocus=alert(1) autofocus y=" — viene correttamente sottoposta a escaping in ogni
posizione (codifica " /   osservata). L'escaping della 2.0.2.1 funziona ovunque
tranne che nel posizionamento del nome dell'attributo. Si tratta di una correzione incompleta dello stesso sink di sostituzione,
non di una lacuna separata in una funzionalità.
# 1. WordPress + WPForms lite 2.0.2.1, create a form with a single-line text field (id 3)
# 2. Set the form's Confirmation message to:
# A: <input value="{field_id="3"}"> B: <input {field_id="3"}>
# 3. Run the PoC (stdlib-only Python 3):
python3 poc.py --url http://target.example/form-page/
Previsto l'output:
[+] field 3 reflects into the confirmation message
[payload (bypass position)] occurrences: LIVE=1 ESCAPED/INERT=2
LIVE -> ...<input onmouseover=alert(poc93485)>...
[control] parent-CVE payload escaped everywhere: YES (fix works at value positions)
=== VERDICT: VULNERABLE — payload reaches attribute-NAME position unescaped ===
poc.py individua automaticamente il form, i campi e il campo che riflette; invia in modo anonimo; e
classifica ogni occorrenza come LIVE (iniezione del nome dell'attributo) vs ESCAPED (valore tra virgolette,
la forma della CVE padre). Codice di uscita 0 = vulnerabile.
Il messaggio di conferma del form, redatto dall'amministratore, deve contenere uno Smart Tag nudo che riflette l'input all'interno del markup in posizione di nome dell'attributo. I ricercatori di Patchstack hanno valutato questo come un pattern di authoring irrealistico; è comunque una posizione documentata di una funzionalità, l' escaping esiste proprio per rendere sicura la sostituzione nel contesto del markup, e l'autore non riceve alcun errore — solo un'iniezione silenziosa.
Codificare = nell'escaping del contesto del markup, oppure analizzare contestualmente il markup di conferma
e consentire la sostituzione degli Smart Tag solo nelle posizioni di testo e di valore di attributo tra virgolette.
poc.py — PoC autonomo (nessun passaggio lato server, nessuna dipendenza)poc.sh — variante minimale basata su curlSolo per test autorizzati e scopi educativi. Il segnalante pubblica questo dopo che un tentativo di segnalazione coordinata è stato rifiutato; il risultato tecnico non è stato contestato.
Ashish Kunwar (@D0rkerdevil)