CVE-2026-82221 - Unauthenticated Reflected Cross-Site Scripting (XSS) no plugin WordPress RegistrationMagic

[research] • [cve] • [xss]

$ cat sumario.txt


Recentemente foi publicada a CVE-2026-82221, uma vulnerabilidade de XSS desautenticado que eu descobri no plugin WordPress RegistrationMagic, afetando versões do plugin <= 6.0.9.8. Nesse artigo, pretendo fornecer uma análise detalhada da vulnerabilidade, incluindo sua causa raíz, impacto potencial e sua prova de conceito.


01. Resumo

O RegistrationMagic, plugin de formulários de registro para WordPress da Metagauss, é vulnerável a Cross-Site Scripting (XSS) refletido não-autenticado em versões ate a 6.0.9.8. A vulnerabilidade CVE-2026-82221 permite que um atacante, sem necessidade de login, injete JavaScript arbitrário na página através do parâmetro de requisição activetab, explorando um caso clássico de escaping aplicado ao contexto errado: o valor é sanitizado como se fosse sair em HTML, mas na verdade é impresso sem aspas dentro de um bloco <script>.

A vulnerabilidade foi corrigida na versão 6.0.9.9, publicada em 27 de agosto de 2026.


   
CVE CVE-2026-82221
CWE CWE-79 — Improper Neutralization of Input During Web Page Generation
CVSS 3.1 7.1 (High) — AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L
Autenticação exigida Nenhuma
Interação do usuário Necessária (vítima precisa clicar em link malicioso)
Componente RegistrationMagic (registration_magic.php)
Versões afetadas <= 6.0.9.8
Versão corrigida 6.0.9.9

02. Impacto

Por se tratar de XSS refletido, o impacto depende de engenharia social (a vítima precisa clicar em um link malicioso), mas alguns dos efeitos possíveis incluem:


O RegistrationMagic é utilizado por mais de 8.000 sites em todo o mundo, ampliando o potencial impacto dessa vulnerabilidade.


03. Detalhes técnicos

A vulnerabilidade explora a ausência de validação de tipo sobre um parâmetro que é, semanticamente, sempre numérico, combinada com o uso de uma função de escaping incompatível com o contexto real de saída (HTML-encoding em um sink JavaScript). A falha está localizada dentro do repositório no arquivo public/controllers/class_rm_front_controller.php, por volta da linha 291, e se propaga até os templates de renderização das áreas de conta.

O parâmetro manipulado, activetab, é lido diretamente da requisição sem qualquer validação de tipo:

// v6.0.9.8 (vulnerável) — class_rm_front_controller.php
if (isset($request->req['activetab']) && $request->req['activetab'] != '') {
    $distinct = true;
    $active_tabs = $request->req['activetab'];
}
$data->active_tab_index = $distinct
    ? $active_tabs
    : (isset($request->req['rm_tab']) ? (int) $request->req['rm_tab'] : 0);

O campo active_tab_index, proveniente diretamente da entrada do usuário, deveria ter sido normalizado pela classe central de sanitização do plugin, includes/class_rm_sanitizer.php. No entanto, activetab não constava na allowlist de parâmetros forçados para inteiro via absint():

// v6.0.9.8 (vulnerável) — class_rm_sanitizer.php
if (in_array($key, array(
    'rm_form_id', 'rm_submission_id',
    'rm_tr', 'rm_reqpage',
    'rm_form_page_no', 'page_no',
    'field_id', 'rm_field_id', 'field_order'))) {
    $request[$key] = absint($value);
}

Como consequência, o valor chega intacto ao sink, dentro dos templates de renderização, onde é impresso sem aspas, como literal numérico, dentro de um bloco <script>:

// v6.0.9.8 (vulnerável) — template_rm_registrations_view.php
activeTabIndex: <?php echo esc_html($data->active_tab_index); ?>
// v6.0.9.8 (vulnerável) — template_rm_front_submissions.php
activeTabIndex: <?php echo wp_kses_post((string)$data->active_tab_index); ?>
...
redirecttosametab(<?php echo wp_kses_post((string)$data->active_tab_index); ?>);

O ponto central da falha é que esc_html() e wp_kses_post() são funções de encoding voltadas para contexto HTML, neutralizam apenas < > & " '. O sink real, entretanto, é um literal JavaScript sem delimitadores de string. Nesse contexto, caracteres como ;, (, ), / e espaço não são neutralizados por essas funções, permitindo que o valor refletido encerre a expressão numérica esperada pelo parser JavaScript e injete código arbitrário controlado pelo atacante.


04. Endpoints afetados

O endpoint afetado corresponde às telas front-end de área de conta do plugin, acessíveis via os shortcodes [RM_Front_Submissions] / [CRF_Submissions], publicados em qualquer página do site que os utilize. O parâmetro activetab (GET/REQUEST), combinado com o parâmetro de roteamento layout_view, determina qual template vulnerável é renderizado:

Rota (layout_view) Template renderizado
(padrão, sem layout_view) template_rm_front_submissions.php
registrations template_rm_registrations_view.php
payments template_rm_user_payments_view.php

Padrão de URL vulnerável:

https://alvo.com/pagina-com-shortcode/?layout_view=registrations&activetab=<PAYLOAD>

05. Prova de Conceito

A prova de conceito pode ser acessada no meu repositório da vulnerabilidade no GitHub disponibilizado em: gabrielftanaka/CVE-2026-82221-PoC

A exploração consiste em induzir a vítima a clicar em um link para uma página do site-alvo que contenha o shortcode de área de conta do RegistrationMagic, com o parâmetro activetab contendo o payload JavaScript. Como o valor é refletido sem sanitização adequada dentro do bloco <script> da resposta, o código é executado no contexto de origem (origin) do site vulnerável, no navegador da vítima.


06. Correção

A Metagauss corrigiu essa vulnerabilidade na versão 6.0.9.9, publicada em 27 de agosto de 2026, por meio de alterações em três pontos do processamento da requisição: o fortalecimento da allowlist de sanitização, a validação explícita de tipo no controller e a correção da função de escaping utilizada no sink. Abaixo é apresentado o diff contendo as principais alterações implementadas pelos desenvolvedores na camada responsável pelo processamento da aba ativa.

--- includes/class_rm_sanitizer.php
  if (in_array($key, array(
      'rm_form_id', 'rm_submission_id',
      'rm_tr', 'rm_reqpage',
+     'rm_reqpage_sub', 'rm_reqpage_pay',
+     'rm_tab', 'activetab',
      'rm_form_page_no', 'page_no',
      'field_id', 'rm_field_id', 'field_order'))) {
      $request[$key] = absint($value);
  }

--- public/controllers/class_rm_front_controller.php
  if (isset($request->req['activetab']) && $request->req['activetab'] != '') {
      $distinct = true;
-     $active_tabs = $request->req['activetab'];
+     $active_tabs = is_numeric($request->req['activetab']) ? absint($request->req['activetab']) : 0;
  }
- $data->active_tab_index = $distinct
-     ? $active_tabs
-     : (isset($request->req['rm_tab']) ? (int) $request->req['rm_tab'] : 0);
+ $data->active_tab_index = $distinct
+     ? absint($active_tabs)
+     : ((isset($request->req['rm_tab']) && is_numeric($request->req['rm_tab'])) ? absint($request->req['rm_tab']) : 0);

--- public/views/template_rm_registrations_view.php
- activeTabIndex: <?php echo esc_html($data->active_tab_index); ?>
+ activeTabIndex: <?php echo absint($data->active_tab_index); ?>

A correção implementa uma abordagem em camadas para mitigar a vulnerabilidade. Inicialmente, o parâmetro activetab passa a integrar a allowlist de campos normalizados pelo sanitizador central, sendo convertido para inteiro logo na entrada da requisição. Em seguida, o controller passou a validar explicitamente, via is_numeric(), que o valor recebido é numérico antes de convertê-lo com absint(). Por fim, nos templates, o sink real do problema, a função de escaping HTML foi substituída por absint(), adequada ao contexto de literal numérico dentro de JavaScript. Dessa forma, qualquer tentativa de inserir marcações HTML ou expressões JavaScript no lugar do índice de aba é automaticamente convertida para um valor inteiro, eliminando por completo o vetor de exploração da vulnerabilidade, independentemente da camada em que a validação seja observada.


07. Recomendações

É recomendado atualizar o RegistrationMagic para a versão 6.0.9.9 ou superior, que corrige a vulnerabilidade por meio da validação adequada do parâmetro activetab e da conversão explícita para valores inteiros antes da renderização. Caso a atualização não seja possível de imediato, recomenda-se a implantação de um WAF/virtual patch capaz de bloquear payloads XSS no parâmetro activetab, além do monitoramento de logs de acesso por requisições contendo esse parâmetro com caracteres como “<”, “script”, “onerror” ou “javascript:”. Como camada adicional de defesa, recomenda-se também a adoção de uma Content Security Policy (CSP) restritiva, reduzindo o impacto de eventuais XSS refletidos não identificados.


08. Conclusão

A exploração dessa vulnerabilidade permite que um atacante não autenticado induza a execução de JavaScript arbitrário no navegador de vítimas do RegistrationMagic, abusando de uma inconsistência entre o contexto de saída real (JavaScript sem aspas) e a função de escaping utilizada (voltada para HTML) sobre o parâmetro activetab. O caso demonstra como a ausência de validação de tipo em um dado semanticamente numérico, combinada com uma allowlist de sanitização incompleta, pode abrir espaço para XSS mesmo em código que aparenta possuir proteções de saída. A correção aplicada pela Metagauss, validando o tipo do dado tanto na entrada quanto na saída, elimina a causa raiz da falha e reforça a importância de escolher a função de escaping de acordo com o contexto real de renderização, e não apenas pela presença de alguma sanitização genérica.


09. Referências