Time-based-side-channel-attacks on secrets – (SiteGround Security <= 1.3.0)

Affected pluginSiteGround Security
Active installs600,000+
Vulnerable version<= 1.3.0
Audited version1.3.0
Fully patched version1.3.2 (Not verified by Snicco)
Recommended remediationUpgrade to version 1.3.2 or higher.

Description


The plugin uses string comparison operators that don’t mitigate time-based attacks in several places where secrets are compared to user input.
A skilled attacker, given enough requests, can abuse this to reverse secrets using time-based-side-channel attacks.

Proof of concept


Validation of 2FA challenge tokens:
(Fixed in 1.3.2)

public function get_2fa_nonce_cookie() {
 // Bail if the cookie doens't exists.
 if ( empty( $_COOKIE['sgs_2fa_login_nonce'] ) ) {
  return;
 }
 // Parse the cookie.
 $cookie_data = explode( '|', $_COOKIE['sgs_2fa_login_nonce'] );
 // Get the user nonce meta.
 $meta_nonce = get_user_meta( $cookie_data[0], 'sgs_2fa_login_nonce', true );
 if ( empty( $meta_nonce ) || empty( $cookie_data[0] ) ) {
  return;
 }
 // Bail if the nonce is invalid.
 if ( $meta_nonce !== $cookie_data[1] ) { // phpcs:ignore
  return;
 }
 // Return the cookie data.
 return $cookie_data;
}

Validation of emergency codes:
(Fixed in 1.3.2)

  public function validate_backup_login( $code, $user ) {
    $codes = get_user_meta( $user, 'sg_security_2fa_backup_codes', true ); // phpcs:ignore

    // Bail if the user doesn't have backup codes.
    if ( empty( $codes ) ) {
      return false;
    }

    $key = array_search( $code, $codes );

    // Bail if the code doesn't exists in the user backup codes.
    if ( false === $key ) {
      return false;
    }

    // Remove the used key.
    unset( $codes[ $key ] );

    // Add additional backup codes to the user meta, if the user has used 4 or more backup codes.
    $this->maybe_add_additional_backup_codes( $codes, $user );

    return true;
  }

Validation of “remember 2FA check” cookie:

public function check_2fa_cookie( $user_login, $user ) {
  // 2FA user cookie name.
  $sg_2fa_user_cookie = 'sg_security_2fa_dnc_cookie';

  // Bail if the cookie doens't exists.
  if ( ! isset( $_COOKIE[ $sg_2fa_user_cookie ] ) ) {
    return false;
  }

  // Parse the cookie.
  $cookie_data = explode( '|', $_COOKIE[ $sg_2fa_user_cookie ] );

  if (
    // If the 2FA is configured for the user.
    1 == get_user_meta( $cookie_data[0], 'sg_security_2fa_configured', true ) && // phpcs:ignore
    get_user_meta( $cookie_data[0], 'sgs_2fa_dnc_token', true ) === $cookie_data[1] // If there is already a cookie with that name and the name matches.
  ) {
    return true;
  }

  return false;
}

Proposed patch


Exclusively use hash_equals to compare secrets.

Timeline


Vendor contactedSeptember 07, 2022
First ResponseSeptember 12, 2022
Fully patched atSeptember 21, 2022 (Not verified by Snicco)
Publicly disclosedApril 24, 2023

Miscellaneous


Leave a Reply

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