
XSS refletido não autenticado no WPForms <= 2.0.2.1 — contorno do escape de Smart Tag da CVE-2026-88996 na posição de nome de atributo
Afetado: WPForms – AI Form Builder for WordPress (gratuito/lite), versão 2.0.2.1 — a mais recente no momento da divulgação (também a versão que contém a correção para CVE-2026-88996). A edição Pro compartilha o renderizador de Smart Tags.
Classe: cross-site scripting refletido não autenticado (CWE-79) com execução de JavaScript verificada, por meio de correção incompleta do CVE-2026-88996 / GHSA-8j5g-9vmv-9mmw.
Descoberto: 2026-09-27 ·
Reportado à Patchstack: 2026-09-27 ·
Recusado: 2026-09-28 ("o problema só aparece quando o administrador do site escreveu uma Smart Tag isolada onde deveria haver o nome de um atributo HTML... não é uma configuração padrão nem um padrão que alguém realisticamente escreveria" — validade técnica não contestada) ·
Público: 2026-09-28.
A correção da versão 2.0.2.1 para o XSS refletido da família CVE-2026-93485 escapa os valores de Smart Tag que são
substituídos na marcação (SmartTags::escape_for_attribute() codifica aspas e
espaços em branco). Isso é eficaz sempre que o valor substituído cai dentro de um valor
de atributo entre aspas. Quando o autor de um formulário coloca uma Smart Tag isolada em uma posição de
nome de atributo na mensagem de Confirmação — por exemplo:
<input {field_id="3"}>
um valor de campo submetido que não contém aspas nem espaços em branco, como
onmouseover=alert(1), é inserido literalmente e torna-se um novo atributo
manipulador de eventos ativo. O escape nunca codifica =, e os nomes de atributos não requerem nem aspas nem
espaços em branco. A confirmação devolvida ao submissor anónimo contém então:
<input onmouseover=alert(1)>
Envios anônimos para admin-ajax.php (action=wpforms_submit,
wp_ajax_nopriv_*) não usam nonce por design para usuários desconectados, portanto trata-se de um
XSS refletido totalmente não autenticado. JavaScript arbitrário é executado dentro da restrição
de token único via onmouseover=eval(String.fromCharCode(...)).
No mesmo formulário, o formato do payload da CVE pai — quebra de aspas de dentro de um valor
entre aspas, por exemplo x" onfocus=alert(1) autofocus y=" — é escapado corretamente em todas
as posições (codificação " /   observada). O escape da 2.0.2.1 funciona em todos os lugares
exceto no posicionamento de nome de atributo. Esta é uma correção incompleta do mesmo sink de
substituição, não uma lacuna de recurso separada.
# 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/
Esperado
[+] 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 descobre automaticamente o formulário, os campos e o campo refletor; envia anonimamente; e
classifica cada ocorrência como LIVE (injeção em nome de atributo) vs ESCAPED (valor entre aspas,
a forma da CVE pai). Código de saída 0 = vulnerável.
A mensagem de Confirmação do formulário, criada pelo administrador, deve conter uma Smart Tag nua que reflete a entrada dentro de marcação na posição de nome de atributo. Os pesquisadores da Patchstack avaliaram isso como um padrão de autoria irrealista; é, no entanto, uma posição de recurso documentada, o escaping existe precisamente para tornar a substituição em contexto de marcação segura, e o autor não recebe erro — apenas injeção silenciosa.
Codificar = no escaping de contexto de marcação, ou analisar contextualmente a marcação de confirmação
e permitir a substituição de Smart Tag apenas em posições de texto e de valor de atributo entre aspas.
poc.py — PoC autocontido (sem etapas no lado do servidor, sem dependências)poc.sh — variante mínima baseada em curlApenas para testes autorizados e fins educacionais. O relator publica isto após uma tentativa de submissão coordenada ter sido recusada; a descoberta técnica não foi contestada.
Ashish Kunwar (@D0rkerdevil)