Critical Account Takeover Vulnerability Patched in LiteSpeed Cache Plugin

Published 5 September 2024
Rafie Muhammad
Security Researcher at Patchstack
Table of Contents

This blog post is about the LiteSpeed plugin vulnerability. If you’re a LiteSpeed user, please update the plugin to at least version 6.5.0.1.

Sign up to Patchstack for free to be first to know about vulnerabilities and automatically protect your website for only $5 / site per month or all your sites for $99 / 50 sites per month.

Patchstack is the official security partner for LiteSpeed Cache. Patchstack is helping with coordinated vulnerability disclosure and patch validation. If you’re a plugin developer and wish to improve the security of your product, consider setting you your own security program for free.

About the LiteSpeed Cache Plugin

The plugin LiteSpeed Cache (free version), which has over 5 million active installations, is known as the most popular caching plugin in the WordPress ecosystem.

This WordPress plugin is an all-in-one site acceleration plugin, featuring an exclusive server-level cache and a collection of optimization features. The plugin supports WordPress Multisite and is compatible with the most popular plugins, including WooCommerce, bbPress, and Yoast SEO.

The security vulnerability

The plugin suffers from an unauthenticated account takeover vulnerability which allows any unauthenticated visitor to gain authentication access to any logged-in users and at worst can gain access to an Administrator level role after which malicious plugins could be uploaded and installed. This vulnerability was discovered as a result of a deeper technical analysis of previously discovered Unauthenticated Privilege Escalation in the same plugin by our alliance member John Blackbourn. In the previous report, we noticed that the hash could also be leaked from a debug log file and we decided to check other possible information leaks on the debug log file.

The vulnerability exploits an HTTP response headers leak on the debug log file which also leaks the “Set-Cookie” header after the users perform a login request. The vulnerability has been assigned CVE-2024-44000 and was fixed in version 6.5.0.1 of the plugin.

The main vulnerable code exists on the function ended:

/**
	* End call of one request process
	* @since 4.7
	* @access public
	*/
public static function ended()
{
	self::debug('Response headers', headers_list());

	$elapsed_time = number_format((microtime(true) - LSCWP_TS_0) * 1000, 2);
	self::debug("End response\n--------------------------------------------------Duration: " . $elapsed_time . " ms------------------------------\n");
}

This function will simply call a self::debug() function with headers_list() data as the debug data parameter. According to the official documentation, the headers_list() function will return a list of response headers sent (or ready to send). This indicates that the plugin will include all of the HTTP response headers to the debug data, including the HTTP response header of Set-Cookie that is triggered when users log in to the WordPress site.

The ended function itself is called from the send_headers_force function which will be triggered from the init hook. We also notice on the plugin setting, there is a feature to Log Cookies. This feature could also leak users request cookie if it’s enabled:

private function _init_request($log_file = null)
{
	if (!$log_file) {
		$log_file = self::$log_path;
	}

	// Check log file size
	$log_file_size = $this->conf(Base::O_DEBUG_FILESIZE);
	if (file_exists($log_file) && filesize($log_file) > $log_file_size * 1000000) {
		File::save($log_file, '');
	}

	// For more than 2s's requests, add more break
	if (file_exists($log_file) && time() - filemtime($log_file) > 2) {
		File::append($log_file, "\n\n\n\n");
	}

	if (PHP_SAPI == 'cli') {
		return;
	}

	$servervars = array(
		'Query String' => '',
		'HTTP_ACCEPT' => '',
		'HTTP_USER_AGENT' => '',
		'HTTP_ACCEPT_ENCODING' => '',
		'HTTP_COOKIE' => '',
		'REQUEST_METHOD' => '',
		'SERVER_PROTOCOL' => '',
		'X-LSCACHE' => '',
		'LSCACHE_VARY_COOKIE' => '',
		'LSCACHE_VARY_VALUE' => '',
		'ESI_CONTENT_TYPE' => '',
	);
	$server = array_merge($servervars, $_SERVER);
	$params = array();

	if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
		$server['SERVER_PROTOCOL'] .= ' (HTTPS) ';
	}

	$param = sprintf('💓 ------%s %s %s', $server['REQUEST_METHOD'], $server['SERVER_PROTOCOL'], strtok($server['REQUEST_URI'], '?'));

	$qs = !empty($server['QUERY_STRING']) ? $server['QUERY_STRING'] : '';
	if ($this->conf(Base::O_DEBUG_COLLAPSE_QS)) {
		$qs = $this->_omit_long_message($qs);
		if ($qs) {
			$param .= ' ? ' . $qs;
		}
		$params[] = $param;
	} else {
		$params[] = $param;
		$params[] = 'Query String: ' . $qs;
	}

	if (!empty($_SERVER['HTTP_REFERER'])) {
		$params[] = 'HTTP_REFERER: ' . $this->_omit_long_message($server['HTTP_REFERER']);
	}

	if (defined('LSCWP_LOG_MORE')) {
		$params[] = 'User Agent: ' . $this->_omit_long_message($server['HTTP_USER_AGENT']);
		$params[] = 'Accept: ' . $server['HTTP_ACCEPT'];
		$params[] = 'Accept Encoding: ' . $server['HTTP_ACCEPT_ENCODING'];
	}
	if ($this->conf(Base::O_DEBUG_COOKIE)) {
		$params[] = 'Cookie: ' . $server['HTTP_COOKIE'];
	}
	if (isset($_COOKIE['_lscache_vary'])) {
		$params[] = 'Cookie _lscache_vary: ' . $_COOKIE['_lscache_vary'];
	}
	if (defined('LSCWP_LOG_MORE')) {
		$params[] = 'X-LSCACHE: ' . (!empty($server['X-LSCACHE']) ? 'true' : 'false');
	}
	if ($server['LSCACHE_VARY_COOKIE']) {
		$params[] = 'LSCACHE_VARY_COOKIE: ' . $server['LSCACHE_VARY_COOKIE'];
	}
	if ($server['LSCACHE_VARY_VALUE']) {
		$params[] = 'LSCACHE_VARY_VALUE: ' . $server['LSCACHE_VARY_VALUE'];
	}
	if ($server['ESI_CONTENT_TYPE']) {
		$params[] = 'ESI_CONTENT_TYPE: ' . $server['ESI_CONTENT_TYPE'];
	}

	$request = array_map(__CLASS__ . '::format_message', $params);

	File::append($log_file, $request);
}

Notice on the function, if $this->conf(Base::O_DEBUG_COOKIE) value is set, then the $server[‘HTTP_COOKIE’] value will be appended to the $params[] and it will be written to the debug log file.

Note that this vulnerability can only be exploited with these certain conditions:

  • Active debug log feature on the LiteSpeed Cache plugin.
  • Has activated the debug log feature once before (not currently active now) and the /wp-content/debug.log file is not purged or removed.

The patch

Since this vulnerability exists because the code tries to include sensitive response header and request cookie data, the LiteSpeed team decided to apply these valid additional protections:

  • Moved the debug log file to the LiteSpeed individual folder at /wp-content/litespeed/debug/.
  • Used random string for log filenames that consist of a substring of MD5 hash value of AUTH_KEY substring.
  • Dropped Log Cookies option on the debug log feature.
  • Removed cookies-related info from the response headers.
  • Added a dummy index.php file inside of the new dedicated debug directory.

The full patch can be seen in this changeset.

Additional recommendations that can be made to make the debug log feature more secure:

  • Apply a proper .htaccess rule to deny direct access to the new log file. The LiteSpeed team already tried to apply a .htaccess rule on the patch, however, the rule implemented is still not proper, and users are still able to directly access the new log file if the users know the new log file name.
  • Perform purge or remove content on the old debug.log file so cookie data that is previously leaked can’t be accessed anymore.

A little note on the patch, we recommend users to analyze their /wp-content/debug.log and purge the file if the site has activated the debug feature on the LiteSpeed Cache plugin once.

Conclusion

This vulnerability highlights the critical importance of ensuring the security of performing a debug log process, what data should not be logged, and how the debug log file is managed. In general, we highly do not recommend a plugin or theme to log sensitive data related to authentication into the debug log file. We also highly recommend the plugin and theme developer to properly store their debug log data inside of a secure debug log file with a decently random log filename and additional .htaccess rule to block direct access.

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.

Timeline

22 August, 2024We found the vulnerability and reached out to the LiteSpeed team regarding the vulnerability.
04 September, 2024LiteSpeed Cache version 6.5.0.1 was released to patch the reported issue.
05 September, 2024Published the vulnerabilities to the Patchstack vulnerability database. Security advisory article publicly released.

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.

The latest in Security advisories

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu