Affected plugin | WPMU Defender |
Active installs | 70,000+ |
Vulnerable version | <= 3.3.0 |
Audited version | 3.2.0 |
Fully patched version | – |
Recommended remediation | Removal of the plugin |
Description
The plugin stores users’ emergency backup codes and TOTP secrets as plaintext in the database.
An attacker that can obtain one of the seemingly never-ending read-only SQL-Injections will be able to bypass all 2FA checks for all users indefinitely.
Proof of concept
The code for retrieving a user’s TOTP secret:
public static function get_user_secret( $user = null ) {
// This should only use in testing.
if ( is_object( $user ) ) {
$user_id = $user->ID;
} else {
$user_id = get_current_user_id();
}
$secret = get_user_meta( $user_id, self::TOTP_SECRET_KEY, true );
if ( ! empty( $secret ) ) {
return $secret;
}
$secret = defender_generate_random_string( self::TOTP_LENGTH, self::TOTP_CHARACTERS );
update_user_meta( $user_id, self::TOTP_SECRET_KEY, $secret );
return $secret;
}
The code for sending an emergency 2FA code per email:
$code = wp_generate_password( 20, false );
update_user_meta( $user->ID, Fallback_Email::FALLBACK_BACKUP_CODE_KEY, [
'code' => $code,
'time' => time(),
]);
The highlighted lines above prove that both values are stored as plaintext in the database. Otherwise, it would not be possible to compare them to user input without any conversion.
An attacker can use a read-only SQLi to bypass all 2FA checks for all users indefinitely (in the case of TOTP secrets, emergency tokens have an expiry).
The below query will give an attacker access to all TOTP secrets for all users.
SELECT user_id, meta_value FROM wp_usermeta WHERE meta_key = 'defenderAuthSecret'
Timeline
Vendor contacted | September 07, 2022 |
First Response | September 08, 2022 |
Fully patched at | – |
Publicly disclosed | April 24, 2023 |
Leave a Reply