
CVE-2024-11972 no Hunk Companion <1.9.0 permite que atacantes não autenticados explorem endpoints inseguros da REST API e instalem plugins vulneráveis, arriscando RCE, SQLi, XSS e backdoors.
O CVE-2024-11972 é uma vulnerabilidade crítica no plugin Hunk Companion para WordPress em versões anteriores à 1.9.0. Esta falha permite que atacantes não autenticados explorem endpoints da REST API com autorização inadequada, possibilitando instalar e ativar plugins do repositório WordPress.org, incluindo aqueles desatualizados ou com vulnerabilidades conhecidas. Explorar esta vulnerabilidade pode levar a riscos graves de segurança, como execução remota de código, injeção SQL, XSS ou backdoors administrativos.
Um exemplo comum de abuso desta vulnerabilidade é com o plugin WP Query Console. Uma vez instalado usando este exploit, o plugin fornece uma interface de console dentro do WordPress onde os usuários podem executar consultas SQL contra o banco de dados do site, permitindo roubo de dados, criação de backdoors e comprometimento completo do banco de dados.
Bibliotecas Python necessárias -> argparse, requests, urljoin
options:
-h, --help show this help message and exit
-u URL, --url URL Base URL of the WordPress site (default: http://localhost/wordpress/).
-p PLUGIN, --plugin PLUGIN
Plugin name to install (default: classic-editor).
<inserir URL do WordPress> -p <inserir nome do plugin>O endpoint vulnerável, /wp-json/hc/v1/themehunk-import, foi inicialmente identificado por Daniel Rodriguez durante uma análise de logs de acesso como parte de uma investigação em andamento.
Usando essa informação, podemos olhar dentro do código fonte do endpoint de importação.
Dentro do arquivo /import/core/class-installation.php podemos ver a seguinte linha:
204 | $temp_file = download_url('https://downloads.wordpress.org/plugin/'.$slug.'.zip');
A classe HUNK_COMPANION_SITES_BUILDER_SETUP gerencia a instalação e ativação de plugins e temas do WordPress, lidando com tipos gratuitos e premium com base nos parâmetros de entrada.
Ela verifica dinamicamente se um plugin ou tema está instalado ou ativo, baixa e descompacta os arquivos necessários se estiverem ausentes e os ativa usando funções principais do WordPress.
Esta funcionalidade codificada permite que o plugin baixe qualquer plugin do repositório WordPress, mesmo aqueles removidos ou descontinuados, dando aos atacantes a oportunidade de aproveitar plugins vulneráveis para exploração.
Para aprofundar no problema, podemos olhar dentro de /import/app/app.php,
vamos comparar entre as diferentes correções introduzidas nas versões 1.8.0 | 1.8.7 | 1.9.0, já que cada uma introduziu outra camada de segurança:
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => '__return_true',
) );
O permission_callback sempre retorna true, isso permite que qualquer usuário ou ator não autenticado acesse o endpoint usando uma requisição POST.
Como não há nenhum tipo de verificação de Nonce ou qualquer tipo de processo de autenticação, um atacante poderia contornar as permissões necessárias e instalar diretamente o plugin desejado.
Na versão 1.8.7, várias melhorias de segurança foram introduzidas para corrigir as falhas presentes em versões anteriores,
mas o endpoint /hc/v1/themehunk-import e a implementação geral ainda apresentam falhas significativas:
public function register_routes() {
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => function () {
// Check if the user is logged in
if ( ! is_user_logged_in() ) {
return new WP_REST_Response( 'Unauthorized: User not logged in', 401 );
}
// Debug: Log the user role and capabilities to see what they have
$current_user = wp_get_current_user();
// error_log( 'Current user: ' . $current_user->user_login );
// error_log( 'User roles: ' . implode( ', ', $current_user->roles ) );
// error_log( 'User capabilities: ' . print_r( $current_user->allcaps, true ) );
// Ensure the user has the 'install_plugins' capability
if ( ! current_user_can( 'install_plugins' ) ) {
return new WP_REST_Response( 'Unauthorized: Insufficient capabilities', 401 );
}
// Get the nonce from the request header
$nonce = $request->get_header('X-WP-Nonce');
// Verify the nonce
if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
return new WP_REST_Response( 'Unauthorized: Invalid nonce', 401 );
}
return true; // Permission granted
Embora esta versão inclua verificações para garantir que o usuário esteja logado (is_user_logged_in()) e verifique se ele possui as capacidades apropriadas (current_user_can('install_plugins')), retornando uma resposta 401 Unauthorized se não tiver, ainda ignora o problema principal que permanece não resolvido.
A lógica de callback do WordPress em relação ao permission_callback é avaliada assim:

Isso significa que uma resposta correta seria uma das seguintes - true | false | WP_Error.
O problema decorre do fato de que o WP_REST_Response retornado não é um booleano ou um WP_Error, como o WordPress não o interpreta como uma negação (false) nem como um erro (WP_Error(), pode aceitá-lo implicitamente e conceder acesso a um usuário não autenticado, essencialmente não resolvendo o problema principal.
A versão 1.9.0 resolveu o problema com sucesso, tornando a vulnerabilidade não mais explorável:
public function register_routes() {
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => function () {
// Check if the user is logged in
if ( ! is_user_logged_in() ) {
return false;
}
// Debug: Log the user role and capabilities to see what they have
$current_user = wp_get_current_user();
// error_log( 'Current user: ' . $current_user->user_login );
// error_log( 'User roles: ' . implode( ', ', $current_user->roles ) );
// error_log( 'User capabilities: ' . print_r( $current_user->allcaps, true ) );
// Ensure the user has the 'install_plugins' capability
if ( ! current_user_can( 'install_plugins' ) ) {
return false;
}
// Get the nonce from the request header
$nonce = $request->get_header('X-WP-Nonce');
// Verify the nonce
if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
return false;
}
return true; // Permission granted
Nesta versão, podemos ver que um valor falso adequado é retornado se o usuário não estiver logado ou não tiver as permissões corretas, garantindo que o permission_callback negue corretamente o acesso não autorizado, tornando o exploit não mais viável.