O que me chamou atenção nessa falha foi que o plugin não esqueceu de criar uma função de autenticação. A função existe, calcula o HMAC e percebe quando o valor enviado está errado. O problema é mais simples: o retorno dela é ignorado.
Começando pelas rotas REST
Algumas rotas sensíveis são registradas com:
'permission_callback' => '__return_true'
Isso permite que a requisição chegue ao callback. Por si só, isso não prova a
vulnerabilidade, então fui seguir o código da rota de blog até addBlog().
Onde a proteção quebra
public function addBlog($request) {
$params = $this->getParams($request);
if (!$params["title"])
return new WP_Error(400, "Param 'title' missing");
$this->checkBlogAuthentication($params);
return $this->admin->saveBlog($params);
}
À primeira vista, checkBlogAuthentication() parece proteger a operação.
A parte importante é ver o que ela devolve quando o HMAC não bate.
$ourhash = hash_hmac(
'sha256',
$title . $siteUrl,
get_option("lupsOnlineLinkNetwerkApiKey")
);
if ($ourhash !== $hash)
return new WP_Error(401, "Authentication error");
Um hash incorreto gera WP_Error. Só que addBlog() não
salva esse retorno e também não usa is_wp_error(). Depois da chamada,
o código simplesmente continua para saveBlog().
Do input até o sink
O request vira $params e esses valores seguem até operações sensíveis
do WordPress. Entre os sinks encontrados estão:
wp_insert_post()
wp_update_post()
wp_delete_post()
wp_insert_term()
O ponto do code review foi verificar se uma falha na autenticação realmente interrompia o caminho até essas funções. Nessa versão, não interrompe.
Teste no laboratório
Para confirmar a hipótese, usei um hash deliberadamente incorreto e uma requisição sem sessão WordPress. Um valor simples para esse tipo de teste é:
{
"title": "CVE 2025 15285 Test",
"hash": "deadbeef"
}
A parte importante não é só receber HTTP 200. Depois da requisição é preciso confirmar se o post foi realmente criado ou alterado. Para exclusão, usei conteúdo criado especificamente para o próprio teste.
Como eu corrigiria
Uma correção direta é fazer o erro controlar o fluxo:
$authentication = $this->checkBlogAuthentication($params);
if (is_wp_error($authentication)) {
return $authentication;
}
return $this->admin->saveBlog($params);
Outra opção seria colocar a checagem no permission_callback, evitando
que o callback que altera o conteúdo seja chamado quando a validação falhar.
O que eu tiro dessa análise
Durante code review, ver uma função com nome como checkAuthentication()
não basta. Vale seguir o retorno até o fim e perguntar o que acontece no caminho de
erro. Nesse caso, a autenticação falha corretamente e mesmo assim a operação continua.