Introduction
A single crafted POST request from a WordPress Subscriber account is enough 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, comes from the plugin's RSVP handler accepting arbitrary user meta keys without validation, which lets an attacker inject the administrator role directly into their own wp_capabilities row.
The Events Calendar for GeoDirectory is an add-on plugin from AyeCode Ltd that extends their GeoDirectory business directory plugin with event listing, scheduling, and RSVP functionality. The add-on has 2,000+ active installs, the parent GeoDirectory core plugin runs on 10,000+ sites, and AyeCode's products collectively serve over 125,000 websites. Sites using this plugin usually run community directories, local business listings, and event calendars, which makes them useful targets for attackers who want 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, in the ajax_ayi_action() AJAX handler (line 154) and the update_ayi_data() function (line 357). The core problem is a 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 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 no allow-list restricts $type to legitimate RSVP values like event_rsvp_yes or event_rsvp_maybe, an attacker can pass any 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 built 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 finds the administrator key and treats it as an active role, granting the attacker all Administrator capabilities. This includes installing plugins, modifying themes, editing all content, managing users, and creating 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. It 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 meant 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 important 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, a harmless RSVP meta key. The $post_id parameter is forced to a non-negative integer via absint(), which makes 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
The developers applied the same allow-list validation a second time inside update_ayi_data() itself. This matters because update_ayi_data() is also called from save_ayi_data() and remove_ayi_data(). Those internal callers use hardcoded safe values today, but 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; }
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 across the class.
The fix removes 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 issues span multiple vulnerability classes, with a 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 |
Three authorization-related vulnerabilities across the AyeCode plugin ecosystem from 2024 through 2026 point to 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.
AyeCode released the patch for CVE-2026-11616 before public disclosure, and the fix shows a solid grasp of the correct remediation pattern, allow-lists over sanitization. The frequency of new vulnerabilities, though, means sites in the GeoDirectory ecosystem should keep 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.
Several factors raise 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 needed.
- Detailed public advisory. The Wordfence advisory provides the exact POST parameters and the resulting meta value, which lowers the effort required for a threat actor to build 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. Once exploitation details are public, privilege escalation vulnerabilities in WordPress plugins tend to be folded quickly 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



