Affected plugin | miniOrange |
Active installs | 20,000+ |
Vulnerable version | <= 5.5.82 |
Audited version | 5.5.82 |
Fully patched version | – |
Recommended remediation | Removal of the plugin |
Description
The plugin stores users’ emergency backup codes as plain text in the database. Furthermore, users’ TOTP secret keys are encrypted, but the encryption keys are stored in the same database as the encrypted ciphertexts.
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
Compromise of TOTP secrets
The plugin uses OpenSSL to encrypt users’ TOTP secret keys before it stores them in the database.
function mo_GAuth_set_secret($user_id,$secret){
global $Mo2fdbQueries;
$key=$this->random_str(8);
update_user_meta( $user_id, 'mo2f_get_auth_rnd_string', $key);
// EDITOR: $key is ultimately passed into openssl_encrpyt.
$secret=mo2f_GAuth_AESEncryption::encrypt_data_ga($secret,$key);
update_user_meta( $user_id, 'mo2f_gauth_key', $secret);
}
For this, the plugin generates a unique encryption key per user.
However, the encryption keys are stored as plaintext alongside the ciphertexts (encrypted TOTP secrets), which defeats the very purpose of encrypting TOTP secrets in the first place.
Using a read-only SQLi, an attacker can indefinitely bypass all two-factor authentication checks by running the following SQL query:
SELECT user_id, meta_value, meta_key FROM wp_usermeta WHERE meta_key = 'mo2f_get_auth_rnd_string' OR meta_key = 'mo2f_gauth_key'
Compromise of emergency codes
The plugin stores 2FA emergency codes encrypted using OpenSSL.
However, the plugin also stores the encryption key in the “wp_options” table.
$key = get_option( 'mo2f_encryption_key' );
$codes_encrypt = MO2f_Utility::encrypt_data($str1, $key);
update_user_meta($id,'chqwetcsdvnvd', $codes_encrypt);
Using an obscure key like “chqwetcsdvnvd” to store the backup codes in the “wp_usermeta” does not make this implementation any safer.
An attacker can compromise all emergency codes for all users by running the following SQL query (through his read-only SQLi):
SELECT user_id, meta_value, meta_key FROM wp_usermeta WHERE meta_key = 'chqwetcsdvnvd'
SELECT option_value FROM wp_options WHERE option_name = 'mo2f_encryption_key'
Timeline
Vendor contacted | September 12, 2022 |
First Response | September 16, 2022 |
Fully patched at | – |
Publicly disclosed | April 24, 2023 |
Leave a Reply