Over 5,000,000 Site Owners Affected by Critical Privilege Escalation Vulnerability Patched in LiteSpeed Cache Plugin


📢 Did you know Wordfence runs a Bug Bounty Program for all WordPress plugin and themes at no cost to vendors? Through October 14th, researchers can earn up to $31,200, for all in-scope vulnerabilities submitted to our Bug Bounty Program! Find a vulnerability, submit the details directly to us, and we handle all the rest.


On August 19th, 2024, the Wordfence Threat Intelligence team discovered that a critical vulnerability was patched in Litespeed Cache, a WordPress plugin installed on over 5,000,000 sites. We found that it is possible for an unauthenticated attacker to spoof their user ID in vulnerable versions, which ultimately makes it possible for them to register as an administrative-level user and completely take over a WordPress site.

Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on August 20th, 2024. Sites using the free version of Wordfence will receive the same protection 30 days later on September 19th, 2024.

While this vulnerability was not reported to the Wordfence Bug Bounty Program, it would’ve likely been awarded a bounty of around $23,400 – $31,200 during our ongoing Superhero Challenge, given the information we know about the vulnerability.

We strongly advise users to update their sites with the latest patched version of Litespeed Cache, version 6.4.1 at the time of this writing, as soon as possible. We have no doubts that this vulnerability will be actively exploited very soon.

Vulnerability Summary from Wordfence Intelligence

Description: LiteSpeed Cache <= 6.3.0.1 – Unauthenticated Privilege Escalation
Affected Plugin: LiteSpeed Cache
Plugin Slug: litespeed-cache
Affected Versions: <= 6.3.0.1
CVE ID: CVE-2024-28000
CVSS Score: 9.8 (Critical)
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Researcher/s: John Blackbourn
Fully Patched Version: 6.4
Bounty Award: Predicted $23,400 – $31,200

The LiteSpeed Cache plugin for WordPress is vulnerable to privilege escalation in all versions up to, and including, 6.3.0.1. This is due to the plugin not properly restricting the role simulation functionality allowing a user to set their current ID to that of an administrator, if they have access to a valid hash which can be found in the debug logs or through brute force. This makes it possible for unauthenticated attackers to spoof their user ID to that of an administrator, and then create a new user account with the administrator role utilizing the /wp-json/wp/v2/users REST API endpoint. In some environments, the crawler may be disabled making this a non-exploitable issue in those cases.

Technical Analysis

LiteSpeed cache is a very popular WordPress plugin designed for caching and optimizing a WordPress website. One feature the plugin offers is the ability to crawl and cache pages as specific authenticated users, utilizing the Crawler Simulation Settings found in the ‘Crawler’ area of the plugin’s settings. Please note that this functionality may be disabled by default on some server configurations which means those sites would be unaffected.

Unfortunately, this functionality was insecurely implemented, making it possible for any unauthenticated user to emulate this functionality and spoof their identity.

The first flaw was present in the async_litespeed_handler() function that had no capability or nonce check making it possible for a user to trigger the function which ultimately triggers a simulated crawl and generates a $hash value to be stored in the options table using the get_hash() function. This $hash value becomes important later.

public static function async_litespeed_handler()
{
    $type = Router::verify_type();

    self::debug('type=' . $type);

    // Don't lock up other requests while processing
    session_write_close();
    switch ($type) {
       case 'crawler':
          Crawler::async_handler();
          break;
       case 'crawler_force':
          Crawler::async_handler(true);
          break;
       case 'imgoptm':
          Img_Optm::async_handler();
          break;
       case 'imgoptm_force':
          Img_Optm::async_handler(true);
          break;
       default:
    }
}

The next flaw is present in the is_role_simulation() function which is triggered via an init hook. This function takes the litespeed_role and litespeed_hash cookie, and if the litespeed_hash cookie value is equal to the databases value set for litespeed_hash, then it will utilize the litespeed_role cookie value to set the current user.

public function is_role_simulation()
{
    if (is_admin()) {
       return;
    }

    if (empty($_COOKIE['litespeed_role']) || empty($_COOKIE['litespeed_hash'])) {
       return;
    }

    Debug2::debug('[Router] starting role validation');

    // Check if is from crawler
    // if ( empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) || strpos( $_SERVER[ 'HTTP_USER_AGENT' ], Crawler::FAST_USER_AGENT ) !== 0 ) {
    //     Debug2::debug( '[Router] user agent not match' );
    //     return;
    // }

    // Hash validation
    $hash = self::get_option(self::ITEM_HASH);
    if (!$hash || $_COOKIE['litespeed_hash'] != $hash) {
       Debug2::debug('[Router] hash not match ' . $_COOKIE['litespeed_hash'] . ' != ' . $hash);
       return;
    }

    $role_uid = $_COOKIE['litespeed_role'];
    Debug2::debug('[Router] role simulate litespeed_role uid ' . $role_uid);

    wp_set_current_user($role_uid);
}

Ultimately, this means that anyone with access to the value of $hash can spoof their user ID to any of their choosing, including administrators. This can be leveraged against select REST API endpoints and be used to create new administrative user accounts. It can also be leveraged against public facing pages that require authentication, such as viewing account areas from other plugins.

The $hash value that is generated through the Str::rrand() function is only 6 characters long, never expires, is limited to 1,000,000 permutations, and utilizes a loose comparison on the value, making it possible to brute force.

public static function get_hash()
{
    // Reuse previous hash if existed
    $hash = self::get_option(self::ITEM_HASH);
    if ($hash) {
       return $hash;
    }

    $hash = Str::rrand(6);
    self::update_option(self::ITEM_HASH, $hash);
    return $hash;
}

The following demonstrates how the $hash value is generated through the Str::rrand() function.

 

In addition, if debugging is enabled, it is possible for an attacker to obtain the hash value easily by triggering an error in the debug log. It is important to note that there may be other ways to obtain the valid hash through the plugin, however, we have only discovered those two methods so far.

 Debug2::debug('[Router] hash not match ' . $_COOKIE['litespeed_hash'] . ' != ' . $hash);

💡Tip for Security Researchers Participating in our Bug Bounty Program

Keep an eye out for the function wp_set_current_user() where it accepts user input, especially if the value can be set via a cookie or header making it possible to spoof the current user with specific requests. When this happens a user may be able to spoof their user ID to that of an administrator and then leverage functionality such as the WordPress REST API to steal sensitive information, install plugins, create administrative user accounts, and more.

This isn’t the first time we’ve seen a vulnerability like this, and we are sure it won’t be the last.

How the Wordfence Firewall Works

The following demonstrates how the Wordfence firewall works to block requests exploiting this vulnerability.

Conclusion

In this blog post, we detailed a privilege escalation vulnerability within the LiteSpeed Cache plugin affecting versions 6.3.0.1 and earlier. This vulnerability makes it possible for unauthenticated threat actors to spoof their user ID, making it possible for the attacker to create new administrative user accounts. This vulnerability was patched in version 6.4 of the plugin.

We encourage WordPress users to verify that their sites are updated to the latest patched version of LiteSpeed Cache as soon as possible considering the critical nature of this vulnerability.

Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on August 20th, 2024. Sites using the free version of Wordfence will receive the same protection 30 days later on September 19th, 2024.

If you know someone who uses this plugin on their site, we recommend sharing this advisory with them to ensure their site remains secure, as this vulnerability poses a significant risk and will likely become exploited.

Did you enjoy this post? Share it!

Comments

3 Comments
  • What is the probability of being successfully exploited?

    • Hi Lili, it's fairly likely for an attacker to be successful in exploiting this issue. While it does require brute force in most cases, the number of possibilities is limited making the chance of success much higher and the hash never expires allowing an attacker to slowly send requests trying to exploit the issue. In addition, if a site owner has debugging enabled, it may be possible to gain access to the hash without brute force.

  • Sounds like I moved from Litespeed Cache to Cloudflare APO just in time. 😅
    It's concerning how ubiquitous the plugin is in it's outdated form; many shared hosting providers automatically install older versions of the plugin (with the auto-update flag disabled) onto websites detected as running Wordpress.