Site takeover through stolen API credentials in combination with SQLi – (miniOrange <= 5.5.82)

| in


Affected pluginminiOrange
Active installs20,000+
Vulnerable version<= 5.5.82
Audited version5.5.82
Fully patched version
Recommended remediationRemoval of the plugin

Description


The plugin uses remote APIs in almost all authentication-related contexts. In addition, the plugin authenticates itself using information stored exclusively as plaintext in the database.

An attacker, armed with a read-only SQLi, can trivially gain access to the API credentials and use them to take over the site in many ways.

Proof of concept


The plugin uses the Mo2f_Api class to communicate with its remote authentication server.

It uses HTTP basic authentication using HTTP headers, which are provided by calling the Mo2f_Api::get_http_header_array method.

    function get_http_header_array() {

        $customerKey = get_option( 'mo2f_customerKey' );
        $apiKey      = get_option( 'mo2f_api_key' );

        /* Current time in milliseconds since midnight, January 1, 1970 UTC. */
        $currentTimeInMillis = Mo2f_Api::get_timestamp();

        /* Creating the Hash using SHA-512 algorithm */
        $stringToHash = $customerKey . $currentTimeInMillis . $apiKey;;
        $hashValue = hash( "sha512", $stringToHash );

        $headers = array(
            "Content-Type" => "application/json",
            "Customer-Key" => $customerKey,
            "Timestamp" => $currentTimeInMillis,
            "Authorization" => $hashValue
        );

        return $headers;
    }

An attacker can obtain both “mo2fa_customerKey” and “mo2f_api_key”
by running the following SQL query through his read-only SQLi:

SELECT option_name, option_value FROM wp_options WHERE option_name = 'mo2f_customerKey' OR option_name = 'mo2f_api_key'

There are dozens of attacker vectors from there on, a couple of examples include:

  • Creating new (miniOrange) users.
  • Changing authentication methods for users.
  • Updating emergency codes for users.
  • Fetching all available user info that miniOranage has about users.

Proposed patch


1. Use API tokens for authentication and store the API tokens encrypted.

2. Implement a secure storage mechanism for retrieving the encryption key.

In WordPress, the options are, from most to least preferred:

  • Reading the encryption key from a file whose name is passed as an environment variable. (Plays great with docker secrets).
  • Reading the encryption key from an environment variable.
  • Reading the encryption key from a constant defined in the wp-config.php file.

A sample implementation could look like this:

final class Secrets {

  public static function get(string $secret_name) :string
  {
    $contents = @file_get_contents($secret_name);
    if(is_string($contents)){
      return $contents;
    }
    if(isset($_SERVER[$secret_name])) {
      return $_SERVER[$secret_name];
    }
    $value = @constant($secret_name);

	if(!$value) {
		throw BrokenEnvironmentException::forMissingSecret($secret_name);
   }
    return $value;
  }

}
}

Timeline


Vendor contactedSeptember 12, 2022
First ResponseSeptember 16, 2022
Fully patched at
Publicly disclosedApril 24, 2023

Miscellaneous


Leave a Reply

Your email address will not be published. Required fields are marked *