
Kalrav AI Agent <= 2.3.3 - Unauthenticated Arbitrary File Upload via kalrav_upload_file AJAX Action
The WordPress Kalrav AI Agent plugin (version 2.3.3 and prior) contains an arbitrary file upload vulnerability that allows unauthenticated users to upload malicious PHP files to the server, potentially leading to remote code execution.
A POC CVE-2025-13374.py is provided to demonstrate a remote attacker uploading shell.php and executing remote code:
python3 ./CVE-2025-13374.py http://techcorp.cc
[+] Target: http://techcorp.cc
[+] File uploaded successfully!
[+] Shell URL: http://techcorp.cc/wp-content/plugins/kalrav-ai-agent/uploads/1763247725-shell.php
[+] Command output:
www-data
The vulnerability exists in the kalrav_upload_file() function at the endpoint. The upload functionality lacks proper file validation, nonce verification, and capability checks. This allows unauthenticated users to upload arbitrary files, including PHP files that can be executed on the server.
/wp-admin/admin-ajax.phpSource: User input from $_FILES['file'] (line 978)
Sink: move_uploaded_file($_FILES['file']['tmp_name'], $target_path) (line 982)
The vulnerability occurs because:
wp_ajax_nopriv_kalrav_upload_file hook (line 967), allowing unauthenticated access.wp_ajax_nopriv_ prefix makes the function accessible to unauthenticated users.$_FILES['file'] is directly processed without validation.basename(): time() . '-' . basename($_FILES['file']['name']) (line 979), which only prevents directory traversal but does not validate file extensions.wp-content/plugins/kalrav-ai-agent/uploads/.File: kalrav-ai-agent.php
Lines: 969-996
add_action('wp_ajax_kalrav_upload_file', 'kalrav_upload_file');
add_action('wp_ajax_nopriv_kalrav_upload_file', 'kalrav_upload_file');
function kalrav_upload_file()
{
// Path to your plugin uploads folder
$upload_dir = plugin_dir_path(__FILE__) . 'uploads/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
if (!empty($_FILES['file']['name'])) {
$file_name = time() . '-' . basename($_FILES['file']['name']);
$target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['file']['tmp_name'], $target_path)) {
wp_send_json_success([
'message' => 'File uploaded successfully!',
'file_path' => $target_path,
'url' => plugins_url('uploads/' . $file_name, __FILE__)
]);
} else {
wp_send_json_error(['message' => 'Failed to move uploaded file.']);
}
} else {
wp_send_json_error(['message' => 'No file uploaded.']);
}
wp_die();
}