
CVE-2024-11972 in Hunk Companion <1.9.0 ermöglicht es nicht authentifizierten Angreifern, unsichere REST-API-Endpunkte auszunutzen und anfällige Plugins zu installieren, was RCE, SQLi, XSS und Backdoors riskiert.
CVE-2024-11972 ist eine kritische Schwachstelle im WordPress-Plugin Hunk Companion in Versionen vor 1.9.0. Dieser Fehler ermöglicht es nicht authentifizierten Angreifern, nicht ordnungsgemäß autorisierte REST-API-Endpunkte auszunutzen, um Plugins aus dem WordPress.org-Repository zu installieren und zu aktivieren, einschließlich veralteter oder bekannter Schwachstellen. Die Ausnutzung dieser Schwachstelle kann zu schwerwiegenden Sicherheitsrisiken führen, wie z. B. Remote Code Execution, SQL-Injection, XSS oder administrativen Hintertüren.
Ein gängiges Beispiel für den Missbrauch dieser Schwachstelle ist das Plugin WP Query Console. Nach der Installation mithilfe dieses Exploits stellt das Plugin eine Konsolenoberfläche innerhalb von WordPress bereit, mit der Benutzer SQL-Abfragen gegen die Datenbank der Website ausführen können, was Datendiebstahl, die Erstellung von Hintertüren und eine vollständige Kompromittierung der Datenbank ermöglicht.
Benötigte Python-Bibliotheken -> argparse, requests, urljoin
options:
-h, --help zeigt diese Hilfemeldung und beendet
-u URL, --url URL Basis-URL der WordPress-Site (Standard: http://localhost/wordpress/).
-p PLUGIN, --plugin PLUGIN
Name des zu installierenden Plugins (Standard: classic-editor).
<WordPress-URL einfügen> -p <Plugin-Namen einfügen>Der anfällige Endpunkt /wp-json/hc/v1/themehunk-import wurde ursprünglich von Daniel Rodriguez während einer Analyse von Zugriffsprotokollen im Rahmen einer laufenden Untersuchung identifiziert.
Mit diesen Informationen können wir den Quellcode des Import-Endpunkts untersuchen.
In der Datei /import/core/class-installation.php sehen wir die folgende Zeile:
204 | $temp_file = download_url('https://downloads.wordpress.org/plugin/'.$slug.'.zip');
Die Klasse HUNK_COMPANION_SITES_BUILDER_SETUP verwaltet die Installation und Aktivierung von WordPress-Plugins und -Themes und behandelt sowohl kostenlose als auch Premium-Typen basierend auf Eingabeparametern.
Sie prüft dynamisch, ob ein Plugin oder Theme installiert oder aktiv ist, lädt die erforderlichen Dateien herunter und entpackt sie, falls sie fehlen, und aktiviert sie mithilfe der WordPress-Kernfunktionen.
Diese hartcodierte Funktionalität erlaubt es dem Plugin, jedes Plugin aus dem WordPress-Repository herunterzuladen, selbst solche, die entfernt oder eingestellt wurden, und gibt Angreifern die Möglichkeit, anfällige Plugins für Angriffe zu nutzen.
Um tiefer in das Problem einzusteigen, können wir in /import/app/app.php schauen.
Vergleichen wir die verschiedenen Korrekturen, die in den Versionen 1.8.0 | 1.8.7 | 1.9.0 eingeführt wurden, da jede eine weitere Sicherheitsebene hinzufügte:
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => '__return_true',
) );
Der permission_callback gibt stets true zurück, was jedem Benutzer oder nicht authentifizierten Akteur den Zugriff auf den Endpunkt über eine POST-Anfrage ermöglicht.
Da es keine Art von Nonce-Überprüfung oder Authentifizierungsprozess gibt, könnte ein Angreifer die erforderlichen Berechtigungen umgehen und das gewünschte Plugin direkt installieren.
In Version 1.8.7 wurden mehrere Sicherheitsverbesserungen eingeführt, um die in früheren Versionen vorhandenen Schwachstellen zu beheben,
aber der Endpunkt /hc/v1/themehunk-import und die gesamte Implementierung weisen immer noch erhebliche Mängel auf:
public function register_routes() {
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => function () {
// Überprüfen, ob der Benutzer eingeloggt ist
if ( ! is_user_logged_in() ) {
return new WP_REST_Response( 'Unauthorized: User not logged in', 401 );
}
// Debug: Logge die Benutzerrolle und -fähigkeiten, um zu sehen, was sie haben
$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 ) );
// Sicherstellen, dass der Benutzer die Fähigkeit 'install_plugins' hat
if ( ! current_user_can( 'install_plugins' ) ) {
return new WP_REST_Response( 'Unauthorized: Insufficient capabilities', 401 );
}
// Den Nonce aus dem Anfrageheader holen
$nonce = $request->get_header('X-WP-Nonce');
// Den Nonce verifizieren
if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
return new WP_REST_Response( 'Unauthorized: Invalid nonce', 401 );
}
return true; // Berechtigung erteilt
Während diese Version Prüfungen enthält, die sicherstellen, dass der Benutzer eingeloggt ist (is_user_logged_in()) und über die entsprechenden Fähigkeiten verfügt (current_user_can('install_plugins')), und eine 401 Unauthorized-Antwort sendet, falls nicht, übersieht sie dennoch das Hauptproblem, das ungelöst bleibt.
Die WordPress-Callback-Logik in Bezug auf permission_callback wird wie folgt ausgewertet:

Das bedeutet, eine korrekte Antwort wäre eine der folgenden: true | false | WP_Error.
Das Problem liegt darin, dass das zurückgegebene WP_REST_Response weder ein Boolean noch ein WP_Error ist. Da WordPress es weder als Verweigerung (false) noch als Fehler (WP_Error) interpretiert, könnte es implizit akzeptiert werden und einem nicht authentifizierten Benutzer Zugriff gewähren, wodurch das Hauptproblem im Wesentlichen nicht gelöst wird.
Version 1.9.0 hat das Problem erfolgreich behoben und die Schwachstelle nicht mehr ausnutzbar gemacht:
public function register_routes() {
register_rest_route( 'hc/v1', 'themehunk-import', array(
'methods' => 'POST',
'callback' => array( $this, 'tp_install' ),
'permission_callback' => function () {
// Überprüfen, ob der Benutzer eingeloggt ist
if ( ! is_user_logged_in() ) {
return false;
}
// Debug: Logge die Benutzerrolle und -fähigkeiten, um zu sehen, was sie haben
$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 ) );
// Sicherstellen, dass der Benutzer die Fähigkeit 'install_plugins' hat
if ( ! current_user_can( 'install_plugins' ) ) {
return false;
}
// Den Nonce aus dem Anfrageheader holen
$nonce = $request->get_header('X-WP-Nonce');
// Den Nonce verifizieren
if ( ! wp_verify_nonce( $nonce, 'hc_import_nonce' ) ) {
return false;
}
return true; // Berechtigung erteilt
In dieser Version sehen wir, dass ein korrekter false-Wert zurückgegeben wird, wenn der Benutzer nicht eingeloggt ist oder nicht über die richtigen Berechtigungen verfügt,
wodurch sichergestellt wird, dass der permission_callback nicht autorisierten Zugriff korrekt verweigert, was den Exploit nicht mehr durchführbar macht.