Really Simple Security Free
Unauthenticated Account Takeover
Really Simple Security Pro
Unauthenticated Account Takeover
Really Simple Security Pro Multisite
Unauthenticated Account Takeover
This blog post is about the Realy Simple Security plugin vulnerability. If you’re a Realy Simple Security user, please update the free, pro, and pro multisite plugin to at least version 9.1.2.
All Patchstack users that have protection enabled have been automatically protected from this vulnerability. Sign up for the free Community account first, to scan for vulnerabilities and apply protection for only $5 / site per month with Patchstack.
For plugin developers, we have security audit services and Enterprise API for hosting companies.
About the Really Simple Security Plugin
The plugin Really Simple Security (free, pro, and pro multisite version), which has over 4 million active installations, is known as one of the most popular 2FA and login protection plugins in WordPress.
This WordPress plugin provides the website with SSL certificate generation, properly enforcing en redirecting to HTTPS, Login Protection, and implementing essential WordPress hardening features.
The security vulnerability
The plugin suffers from an unauthenticated account takeover vulnerability which allows any unauthenticated visitor to take over and login into any account only by knowing the user ID. The worst case is where the attacker is able to log in to an Administrator account. This is possible due to improper error handling on the login nonce check process.
The vulnerability exploits the skip onboarding feature on the REST endpoint of the plugin. The vulnerability was initially discovered by István Márton from WordFence. The vulnerability has been assigned CVE-2024-10924 and was fixed in version 9.1.2 of the plugin.
Patchstack has issued a vPatch to mitigate this vulnerability, and so far we’ve already seen several attempts to exploit it on the Free and Pro versions of the plugin, all originating from the IP 81.94.156.110.
Unauthenticated Account Takeover
The underlying issue is located in the check_login_and_get_user function:
/**
* Verifies a login nonce, gets user by the user id, and returns an error response if any steps fail.
*
* @param int $user_id The user ID.
* @param string $login_nonce The login nonce.
*
* @return WP_User|WP_REST_Response
*/
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 );
}
/**
* Get the user by the user ID.
*
* @var WP_User $user
*/
$user = get_user_by( 'id', $user_id );
return $user;
}
The $login_nonce is not properly checked and it will return a new WP_REST_Response object if the check is failed without further handling on the WP_REST_Response error object. This will allow the check_login_and_get_user function to continue to process and return $user even if the login nonce is invalid.
The function above can be called from skip_onboarding function which handle request to /wp-json/reallysimplessl/v1/two_fa/skip_onboarding REST endpoint :
/**
* Skips the onboarding process for the user.
*
* @param WP_REST_Request $request The REST request object.
*
* @return WP_REST_Response The REST response object.
*/
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.
$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 final function that allows users to be able to log in with only user ID is on :
/**
* Sets the authentication cookie and returns a success response.
*
* @param int $user_id The user ID.
* @param string $redirect_to The redirect URL.
*
* @return WP_REST_Response
*/
private function authenticate_and_redirect( int $user_id, string $redirect_to = '' ): WP_REST_Response {
// Okay checked the provider now authenticate the user.
wp_set_auth_cookie( $user_id, true );
// Finally redirect the user to the redirect_to page or to the home page if the redirect_to is not set.
$redirect_to = $redirect_to ?: home_url();
return new WP_REST_Response( array( 'redirect_to' => $redirect_to ), 200 );
}
Note that the vulnerability can only be exploited if the 2FA feature is enabled (disabled by default).
The patch
Since this vulnerability exists because of improper error handling when checking the login nonce, the Realy Simple Security team decided to terminate the program flow if the login nonce failed. The full patch can be seen in this changeset.
Conclusion
This vulnerability highlights the critical importance of ensuring the process of handling custom user authentication and also the importance of properly handling an error if a data check fails. We recommend plugin developer properly check access for a custom authentication process and make sure that users can’t log in to any account over which they don’t have control.
Want to learn more about finding and fixing vulnerabilities?
Explore our Academy to master the art of finding and patching vulnerabilities within the WordPress ecosystem. Dive deep into detailed guides on various vulnerability types, from discovery tactics for researchers to robust fixes for developers. Join us and contribute to our growing knowledge base.
Help us make the Internet a safer place
Making the WordPress ecosystem more secure is a team effort, and we believe that plugin developers and security researchers should work together.
- If you’re a plugin developer, join our mVDP program that makes it easier to report, manage and address vulnerabilities in your software.
- If you’re a security researcher, join Patchstack Alliance to report vulnerabilities & earn rewards.
This article was originally published on PatchStack. Detect & patch vulnerabilities in your WordPress websites Signup Now.