wordpress / stored xss / patch review

Paytium 5.0.3 → 5.0.5

Código de desconto refletido no painel sem escape em 5.0.3; correção confirmada em 5.0.5.

Esta é uma análise de regressão entre o Paytium 5.0.3 e o Paytium 5.0.5. O objetivo foi verificar se um valor controlável relacionado a descontos alcançava um sink HTML inseguro e se a versão posterior neutralizava o mesmo caminho.

Conclusão curta: o caminho do código de desconto era vulnerável a XSS armazenado no painel administrativo em 5.0.3. Na cópia 5.0.5 revisada, o sink passou a usar esc_html(). Portanto, este caso é documentado como corrigido em 5.0.5, sem alegar uma nova CVE.

O fluxo vulnerável em 5.0.3

No processamento do pagamento, o Paytium monta os dados do desconto a partir do formulário. Entre esses dados está o código do desconto, associado ao campo discount_code.

$discount_data = array(
    'discount_code' => $_POST['pt-discount']['code'],
    // ...
);

O valor é persistido junto aos dados do pagamento. Mais tarde, a tela de detalhes da assinatura recupera esse valor por meio de $payment->get_discount_code() e o concatena diretamente no HTML.

echo $payment->get_discount_value()
    . ' (code: ' . $payment->get_discount_code() . ')';

Esse echo é o sink final: o conteúdo controlado termina em um contexto de texto HTML sem escape de saída. Quando um administrador abre o detalhe da assinatura que contém o valor armazenado, o navegador interpreta a marcação como parte da página.

entrada do código de desconto ↓ dados do pagamento ↓ persistência do pagamento ↓ get_discount_code() ↓ echo dentro de subscription-details.php ↓ HTML do painel administrativo

Correção confirmada em 5.0.5

Na versão 5.0.5 comparada no laboratório, o mesmo ponto foi alterado para escapar o valor antes da saída:

echo esc_html(
    $payment->get_discount_value()
    . ' (code: ' . $payment->get_discount_code() . ')'
);

A correção é relevante porque ocorre no sink e no contexto correto. Mesmo que um valor antigo permaneça no banco de dados, ele deixa de ser interpretado como HTML nessa tela. O resultado esperado é a exibição literal dos caracteres especiais, em vez da execução de JavaScript.

PoC segura para laboratório

A validação foi feita apenas em uma instalação própria. O marcador utilizado para confirmar a hipótese foi um valor inofensivo com caracteres HTML:

<img src=x onerror=alert(document.domain)>

Em 5.0.3, o valor armazenado alcançava o detalhe da assinatura como marcação. Em 5.0.5, a mesma sequência deve aparecer como texto escapado, por exemplo &lt;img ...&gt;, sem execução. A PoC não deve ser usada contra sites de terceiros e não inclui cookies, tokens ou credenciais.

Boas práticas de correção

A correção mínima é aplicar escape contextual no ponto de saída: esc_html() para texto, esc_attr() para atributos e esc_url() para URLs. Também é recomendável validar a estrutura dos dados na entrada, restringir chaves de metadados a uma allowlist e testar cada caminho de persistência até seu sink final.

← voltar