Wordpress: Remote Admin Reset Password Exploit | Wie es dazu kommen konnte

Wieder einmal eine Lücke in Wordpress. Ich mach mir mal die Mühe und zeige die Umstände wie es dazu kommen konnte.

In der wp-login.php gibt es die Möglichkeit mit den Paramatern: ?action=rp&key=LANGER_KEY ein Passwort zurücksetzten zu lassen. Bedingung hierfür ist jedoch dass der key in der Datenbank gefunden wurde.

Code:

Schauen wir uns mal den Code genauer an:

function reset_password($key) {
global $wpdb;

$key = preg_replace('/[^a-z0-9]/i', '', $key);

if ( empty( $key ))
return new WP_Error('invalid_key', __('Invalid key'));

$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users WHERE user_activation_key = %s", $key));
if ( empty( $user ) )
return new WP_Error('invalid_key', __('Invalid key'));

// Generate something random for a password...
$new_pass = wp_generate_password();

do_action('password_reset', $user, $new_pass);

...

Erklärung:

Der Parameter $key kommt direkt von $_GET['key']. Dieser wird jetzt mit preg_replace() aller Zeichen ausser a-zA-Z0-9 bereinigt. Und hier liegt auch schon das Problem. Dummerweise akzeptiert preg_replace() auch Arrays als Parameter wo dann jeder einzelne Eintrag des Arrays abgearbeitet wird. Link zur PHP Doku: http://de.php.net/manual/en/function.preg-replace.php.

Was manche nicht wissen, es ist möglich auch ein Array per GET zu übergeben. Man müsste dazu in der URL schreiben: &key[]=eintrag.

Wenn man dies nun macht wird nach dem preg_replace $key ein Array sein mit genau einem Element drin. Da aber Arrays mit mindestens einem Element drin als true gewertet werden liefert empty($key) hier false und es wird KEINE Fehlermeldung ausgespuckt.
Was danach passiert ist teils auch nicht so ganz offensichtlich. Im Aufruf von $wpdb->prepare wird $key benutzt welches, da es ein Array und kein String ist nicht korrekt geparsed welches zu folgender Query führt: "SELECT * FROM jb_v1_users WHERE user_activation_key = ''". Es steht jetzt am Schluss = '' - diese Query findet also den ersten Datensatz in der user Tabelle wo die Spalte user_activation_key leer ist. Dies ist meistens der Admin. Im Nachfolgenden Code wird jetzt das Passwort des Admins geändert was auch schon den ganzen Exploit ausmacht. Der Admin kann sich vorerst nicht mehr einloggen.

Beheben lässt sich das ganze indem der Admin einfach seinen Passwort Hash in der Datenbank ändert und sich so wieder einloggen kann.

Fix:

Um diese Lücke zu fixen gibt es mehrere Möglichkeiten:

Wir machen aus:

$key = preg_replace('/[^a-z0-9]/i', '', $key);
$key = preg_replace('/[^a-z0-9]/i', '', (string)$key);

was uns dann immer einen String zurückliefern wird. Zwar würde im Fall dass $key ein Array ist nachher "Array" drin stehen, aber diesen Key wird bestimmt keiner als user_activation_key haben.

Oder wir machen aus:

if ( empty( $key ))
return new WP_Error('invalid_key', __('Invalid key'));
if ( empty( $key ) || is_array($key))
return new WP_Error('invalid_key', __('Invalid key'));

welches die Fehlermeldung ausgeben wird wenn $key fälschlicherweise ein Array ist.

Der Bug ist echt nicht auf den ersten Blick zu sehen, daher auch keine Schande über die Wordpress Programmierer. Mein Rat an dieser Stelle, lieber ein paar mal mehr casten als zu wenig.

No related posts.


 
 
 

Die Kommentarfunktion zu diesem Beitrag wurde deaktiviert.