Introduction
A single crafted POST request from a WordPress Subscriber account is all it takes to gain full Administrator access on any site running the Events Calendar for GeoDirectory plugin version 2.3.28 or earlier. The vulnerability, tracked as CVE-2026-11616 with a CVSS 3.1 score of 8.8, stems from the plugin's RSVP handler accepting arbitrary user meta keys without validation, allowing an attacker to inject the administrator role directly into their own wp_capabilities row.
The Events Calendar for GeoDirectory is an add-on plugin developed by AyeCode Ltd that extends their flagship GeoDirectory business directory plugin with event listing, scheduling, and RSVP functionality. The add-on has 2,000+ active installs, while the parent GeoDirectory core plugin runs on 10,000+ sites, and AyeCode's products collectively serve over 125,000 websites. Sites using this plugin typically operate community directories, local business listings, and event calendars, making them attractive targets for attackers seeking to compromise WordPress installations with active user bases.
Technical Information
Root Cause: Sanitization Without Semantic Validation
The vulnerability lives in class-geodir-event-ayi.php, specifically in the ajax_ayi_action() AJAX handler (line 154) and the update_ayi_data() function (line 357). The core problem is a fundamental confusion between sanitization and validation. The handler applies strip_tags(esc_sql()) to user input, which prevents HTML injection and SQL injection, but does nothing to restrict which meta keys can be written. A string like wp_capabilities contains no HTML tags and no SQL metacharacters, so it passes through both functions completely untouched.
In the vulnerable version (2.3.28), the AJAX handler processes POST parameters like this:
// Vulnerable code in v2.3.28: $action = strip_tags(esc_sql($_POST['btnaction'])); $type = strip_tags(esc_sql($_POST['type'])); $post_id = strip_tags(esc_sql($_POST['postid'])); $gde = strip_tags(esc_sql($_POST['gde']));
The $type value is then forwarded to update_ayi_data(), which uses it as the meta key in a call to update_user_meta($current_user->ID, $rsvp_args['type'], $posts). Because there is no allow-list restricting $type to legitimate RSVP values like event_rsvp_yes or event_rsvp_maybe, an attacker can pass any arbitrary string as the meta key.
Exploitation Flow
The attack proceeds in four steps:
Step 1: Craft the malicious request. An authenticated user with Subscriber privileges sends a POST request to the WordPress AJAX endpoint (wp-admin/admin-ajax.php) targeting the ajax_ayi_action handler. The request includes type=wp_capabilities and postid=administrator.
Step 2: Bypass sanitization. The strip_tags() function finds no HTML tags in wp_capabilities or administrator. The esc_sql() function finds no SQL metacharacters. Both values pass through unchanged.
Step 3: Overwrite user meta. The update_ayi_data() function calls update_user_meta($current_user->ID, 'wp_capabilities', $posts), where $posts is constructed to include the attacker's existing subscriber role plus the injected administrator value. The resulting meta value is ['subscriber'=>true,'administrator'=>'administrator'].
Step 4: Role activation. On the attacker's next request to the WordPress site, WP_User::get_role_caps() iterates over the wp_capabilities array. It encounters the administrator key and treats it as an active role, granting the attacker all Administrator capabilities. This includes the ability to install plugins, modify themes, edit all content, manage users, and create new admin accounts.
CVSS Vector Breakdown
The CVSS 3.1 vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H reflects the severity accurately:
| Component | Value | Rationale |
|---|---|---|
| Attack Vector | Network | Exploitable via standard HTTP POST |
| Attack Complexity | Low | No special conditions or race windows |
| Privileges Required | Low | Subscriber account sufficient |
| User Interaction | None | No victim action needed |
| Scope | Unchanged | Stays within WordPress authorization boundary |
| Confidentiality | High | Full admin access to all site data |
| Integrity | High | Can modify any content, users, or settings |
| Availability | High | Can delete content, deactivate plugins, or lock out admins |
CWE-269: Improper Privilege Management
This vulnerability is classified under CWE-269, which MITRE defines as occurring when "the product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor." The plugin modifies a user's privilege metadata without verifying that the meta key being written is a legitimate, expected key for the RSVP feature. This is a textbook instance of the weakness: the actor (a Subscriber) expands their own sphere of control to Administrator level through a mechanism the plugin never intended to expose.
Patch Information
The vulnerability was patched in version 2.3.29 of the Events Calendar for GeoDirectory plugin, delivered via WordPress Trac changeset 3533585. The fix is clean and thorough, replacing open-ended string sanitization with closed allow-lists at both the AJAX boundary and the data layer.
The Core Fix: Ternary Allow-Lists
The most critical change replaces the permissive strip_tags(esc_sql()) calls with strict ternary expressions that force each parameter into one of exactly two permitted values:
// BEFORE (v2.3.28 — vulnerable): $action = strip_tags(esc_sql($_POST['btnaction'])); $type = strip_tags(esc_sql($_POST['type'])); $post_id = strip_tags(esc_sql($_POST['postid'])); $gde = strip_tags(esc_sql($_POST['gde'])); // AFTER (v2.3.29 — patched): $action = 'add' === $_POST['btnaction'] ? 'add' : 'remove'; $type = 'event_rsvp_yes' === $_POST['type'] ? 'event_rsvp_yes' : 'event_rsvp_maybe'; $post_id = absint($_POST['postid']); $gde = !empty($_POST['gde']) ? sanitize_key($_POST['gde']) : '';
With this change, the $type parameter can only ever be event_rsvp_yes or event_rsvp_maybe. An attacker sending type=wp_capabilities would have it silently normalized to event_rsvp_maybe, which is a harmless RSVP meta key. The $post_id parameter is forced to a non-negative integer via absint(), making string injection of administrator impossible.
Date Validation Guard
The patch also added input validation for the $gde parameter:
if ( !empty($gde) && !geodir_event_is_date( $gde ) ) { wp_send_json_error( array( 'message' => __( 'Invalid date provided.', 'geodirectory' ) ) ); }
Defense in Depth at the Data Layer
Critically, the developers applied the same allow-list validation a second time inside update_ayi_data() itself. This is important because update_ayi_data() is also called from save_ayi_data() and remove_ayi_data(). Although those internal callers use hardcoded safe values today, locking down the function at its own boundary protects against future code changes that might introduce new call paths:
// Added at the top of update_ayi_data() in v2.3.29: $rsvp_args['action'] = 'add' === $rsvp_args['action'] ? 'add' : 'remove'; $rsvp_args['type'] = 'event_rsvp_yes' === $rsvp_args['type'] ? 'event_rsvp_yes' : 'event_rsvp_maybe'; $rsvp_args['post_id'] = absint($rsvp_args['post_id']); $rsvp_args['gde'] = !empty($rsvp_args['gde']) ? sanitize_key($rsvp_args['gde']) : false; if ( ! isset( $rsvp_args['action'] ) || ! isset( $rsvp_args['type'] ) || ! isset( $rsvp_args['post_id'] ) ) { return; }
Additionally, throughout the rest of the file, all remaining uses of sanitize_text_field( strip_tags(...) ) on the $gde variable were replaced with sanitize_key(), tightening input handling consistently across the class.
The fix completely eliminates the privilege escalation vector by ensuring the user meta key used in update_user_meta() can never be anything other than the two legitimate RSVP keys. The current release is version 2.3.30 as of June 4, 2026.
Affected Systems and Versions
The vulnerability affects the Events Calendar for GeoDirectory WordPress plugin in all versions up to and including 2.3.28.
| Detail | Value |
|---|---|
| Affected Plugin | Events Calendar for GeoDirectory |
| Affected Versions | All versions through 2.3.28 |
| Patched Version | 2.3.29 |
| Current Version | 2.3.30 |
| WordPress Plugin Slug | events-for-geodirectory |
| Vulnerable File | includes/class-geodir-event-ayi.php |
| Required Authentication | Subscriber level or above |
| Prerequisite | Plugin must be active; user registration must be enabled (or attacker must already have an account) |
Sites running version 2.3.29 or later are not affected. The plugin has 2,000+ active installs, and the parent GeoDirectory ecosystem serves 10,000+ sites with the core plugin.
Vendor Security History
AyeCode Ltd's GeoDirectory core plugin has 17 documented vulnerabilities in the Wordfence Intelligence database. The pattern of issues spans multiple vulnerability classes, with a notable concentration in authorization and privilege management flaws. Recent examples include:
| Vulnerability | Plugin | Affected Version | Patched Version | Date |
|---|---|---|---|---|
| Authenticated (Contributor+) Stored XSS | GeoDirectory | <= 2.3.84 | Not specified | 2024 |
| Cross-Site Request Forgery (CSRF) | GeoDirectory | <= 2.8.149 | 2.8.150 | January 2026 |
| Missing Authorization | GeoDirectory | <= 2.8.157 | Not specified | 2026 |
| Authenticated (Subscriber+) Privilege Escalation | Events Calendar for GeoDirectory | <= 2.3.28 | 2.3.29 | June 2026 |
The convergence of three authorization-related vulnerabilities across the AyeCode plugin ecosystem in 2024 through 2026 suggests a systemic gap in the vendor's approach to authorization and privilege checking. The CSRF vulnerability in the core plugin patched in January 2026 and the Missing Authorization issue in version 2.8.157 are consistent with the same class of weakness that underlies CVE-2026-11616.
On the positive side, AyeCode released the patch for CVE-2026-11616 before public disclosure, and the fix demonstrates a solid understanding of the correct remediation pattern (allow-lists over sanitization). The frequency of new vulnerabilities, however, suggests that sites in the GeoDirectory ecosystem should maintain rigorous update practices and consider defense-in-depth monitoring.
Threat Intelligence
As of the NVD publication date of June 9, 2026, there is no verified evidence of active exploitation in the wild for CVE-2026-11616. The Wordfence advisory does not mention confirmed exploitation, no public proof-of-concept code has been verified, and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) Catalog.
That said, several factors elevate the exploitation risk going forward:
- Low barrier to exploitation. The attack requires only a Subscriber account and a single HTTP POST request. No chaining with other vulnerabilities is necessary.
- Detailed public advisory. The Wordfence advisory provides the exact POST parameters and the resulting meta value, which significantly lowers the effort required for a threat actor to develop a working exploit.
- WordPress plugin targeting at scale. WordPress plugin vulnerabilities are a primary target for automated exploitation campaigns. Wordfence has documented over 4.6 million attacks exploiting WordPress plugin vulnerabilities in recent 30-day periods, and privilege escalation flaws are among the most valuable to attackers because they provide persistent, high-privilege access.
No specific threat actors have been identified as targeting this CVE. The WordPress plugin exploitation landscape is dominated by automated botnet activity rather than named APT groups. Historical patterns indicate that once exploitation details are public, privilege escalation vulnerabilities in WordPress plugins are rapidly integrated into mass-scanning toolkits.
References
- Wordfence Advisory: Events Calendar for GeoDirectory <= 2.3.28 Privilege Escalation
- NVD: CVE-2026-11616
- CVE Record: CVE-2026-11616
- WordPress Trac: Vulnerable code at line 154 (v2.3.28)
- WordPress Trac: Vulnerable code at line 357 (v2.3.28)
- WordPress Trac: Changeset 3533585 (patch)
- WordPress SVN: class-geodir-event-ayi.php v2.3.28 (vulnerable)
- WordPress SVN: class-geodir-event-ayi.php v2.3.29 (patched)
- WordPress Plugin Repository: Events Calendar for GeoDirectory
- WordPress Plugin Repository: GeoDirectory
- Wordfence: GeoDirectory Vulnerability History
- CWE-269: Improper Privilege Management
- CISA Known Exploited Vulnerabilities Catalog
- AyeCode Ltd



