CVE-2026-7249

Location Weather <= 3.0.2 - Missing Authorization to Authenticated (Contributor+) Block Settings Modification and Cache Purging

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.0.3
Patched in
1d
Time to patch

Description

The Location Weather plugin for WordPress is vulnerable to unauthorized modification of data due to missing capability checks on the `splw_update_block_options()` and `lwp_clean_weather_transients()` functions in all versions up to, and including, 3.0.2. This makes it possible for authenticated attackers, with Contributor-level access and above, to disable all weather blocks and purge all weather cache transients. The nonce required for these actions is exposed to all authenticated users via `wp_localize_script()` on the `init` hook.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.2
PublishedMay 21, 2026
Last updatedMay 22, 2026
Affected pluginlocation-weather

What Changed in the Fix

Changes introduced in v3.0.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-7249 - Location Weather Missing Authorization ## 1. Vulnerability Summary The **Location Weather** plugin (versions <= 3.0.2) contains a missing authorization vulnerability in two AJAX functions: `splw_update_block_options()` and `lwp_clean_weather_transients(…

Show full research plan

Exploitation Research Plan: CVE-2026-7249 - Location Weather Missing Authorization

1. Vulnerability Summary

The Location Weather plugin (versions <= 3.0.2) contains a missing authorization vulnerability in two AJAX functions: splw_update_block_options() and lwp_clean_weather_transients(). Both functions are registered via wp_ajax_ hooks in the ShapedPlugin\Weather\Admin\AdminDashboard\Splw_Blocks_Page_Wrapper class.

While these functions perform nonce verification, they fail to implement current_user_can() checks. Consequently, any authenticated user with Contributor-level permissions or higher—who can access the WordPress Block Editor—can obtain the necessary nonce and invoke these actions to modify global plugin settings (disabling blocks) or purge cached weather data.

2. Attack Vector Analysis

  • Endpoints: /wp-admin/admin-ajax.php
  • AJAX Actions:
    1. splw_update_block_options (for setting modification)
    2. lwp_clean_weather_transients (for cache purging)
  • Required Parameters:
    • action: splw_update_block_options or lwp_clean_weather_transients
    • nonce: A valid nonce for the action splw_block_api_nonce
    • options: (For splw_update_block_options) An array or JSON object containing the block settings to modify.
  • Authentication: Authenticated (Contributor+).
  • Preconditions: The attacker must be able to load a page where the plugin enqueues its block editor scripts to retrieve the nonce.

3. Code Flow

  1. Entry Point: The constructor of Splw_Blocks_Page_Wrapper registers the hooks:
    • add_action( 'wp_ajax_splw_update_block_options', array( $this, 'splw_update_block_options' ) );
    • add_action( 'wp_ajax_lwp_clean_weather_transients', array( $this, 'lwp_clean_weather_transients' ) );
  2. Nonce Exposure: In includes/Blocks/Blocks.php, the splw_scripts_enqueue() function (hooked to enqueue_block_assets) calls wp_localize_script():
    wp_localize_script(
        'spl_weather_editor_js',
        'splWeatherBlockLocalize',
        array(
            'ajaxUrl'       => admin_url( 'admin-ajax.php' ),
            'blockApiNonce' => wp_create_nonce( 'splw_block_api_nonce' ),
            'blockOptions'  => get_option( 'splw_blocks_visibility_options' ),
            // ...
        )
    );
    
  3. Execution Path:
    • The user sends an AJAX request with action=splw_update_block_options.
    • The function (in Splw_Blocks_Page_Wrapper.php) calls check_ajax_referer( 'splw_block_api_nonce', 'nonce' ).
    • It proceeds to call update_option( 'splw_blocks_visibility_options', ... ) using user-supplied data without verifying the user has manage_options or the location_weather_access_capability.

4. Nonce Acquisition Strategy

The nonce is localized specifically for the block editor environment. Since Contributors can create/edit posts, they can load the Gutenberg editor.

  1. Access Editor: Navigate to the "Add New Post" page (/wp-admin/post-new.php).
  2. Locate Script: The plugin enqueues spl_weather_editor_js.
  3. Extract Variable: Use browser_eval to extract the nonce from the splWeatherBlockLocalize global object.
    • Target Variable: window.splWeatherBlockLocalize.blockApiNonce

5. Exploitation Strategy

Phase 1: Disable All Weather Blocks

  1. Authenticate as a Contributor user.
  2. Retrieve Nonce:
    • Navigate to wp-admin/post-new.php.
    • Extract nonce: browser_eval("window.splWeatherBlockLocalize?.blockApiNonce").
  3. Send Malicious Update:
    • URL: http://[target]/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body:
      action=splw_update_block_options&nonce=[NONCE]&options[location_weather_block]=false&options[weather_forecast_block]=false
      
      (Note: Parameter names for options are inferred based on standard plugin patterns; if the request expects JSON, the payload should be adjusted accordingly.)

Phase 2: Purge Weather Transients

  1. Send Purge Request:
    • URL: http://[target]/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body:
      action=lwp_clean_weather_transients&nonce=[NONCE]
      

6. Test Data Setup

  1. Plugin Configuration: Install "Location Weather" 3.0.2. Ensure at least one weather block visibility option is currently true.
  2. User Creation: Create a user with the contributor role.
  3. Cache Generation: Populate some transients by viewing a weather block on the frontend.
    • Run wp transient set sp_open_weather_test "cache_data" 3600.

7. Expected Results

  • Block Modification: The HTTP response should be a success (likely wp_send_json_success or 1). The WordPress option splw_blocks_visibility_options will be updated with the provided (disabled) values.
  • Cache Purge: The HTTP response should be a success. Database queries will execute to delete transients matching _transient_sp_open_weather_%.

8. Verification Steps

  1. Check Options:
    • wp option get splw_blocks_visibility_options
    • Verify that the values now reflect the "false" settings sent in the exploit.
  2. Check Transients:
    • wp db query "SELECT * FROM wp_options WHERE option_name LIKE '%_transient_sp_open_weather_%'"
    • Verify that the result set is empty.

9. Alternative Approaches

  • Setting Modification via JSON: If the function uses json_decode on $_POST['options'], the request should be changed to:
    action=splw_update_block_options&nonce=[NONCE]&options={"location_weather_block":false}
  • Common Option Key: If the options parameter is not used, check for splw_update_setting_options (also registered in the source) which may allow broader configuration changes.
  • Nonce Discovery: If post-new.php doesn't load the script, try wp-admin/edit.php?post_type=location_weather&page=splw_admin_dashboard if the Contributor has been granted filtered access to that page.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Location Weather plugin for WordPress is vulnerable to unauthorized modification of data and cache purging due to missing capability checks on several AJAX functions, including splw_update_block_options and lwp_clean_weather_transients. Authenticated attackers with Contributor-level permissions or higher can obtain a required nonce via the Block Editor and use it to disable plugin features or flush weather transients.

Vulnerable Code

// includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php L255
public function splw_update_block_options() {
    $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
    if ( ! wp_verify_nonce( $nonce, 'splw_block_api_nonce' ) ) {
        wp_send_json_error( __( 'Invalid nonce.', 'location-weather' ) );
    }

    $options = isset( $_POST['options'] ) ? (array) $_POST['options'] : array();

    if ( ! empty( $options ) ) {
        update_option( 'splw_blocks_visibility_options', $options );
    }

    wp_send_json_success();
}

---

// includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php L330
public function lwp_clean_weather_transients() {
    $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
    if ( ! wp_verify_nonce( $nonce, 'splw_block_api_nonce' ) ) {
        wp_send_json_error( __( 'Invalid nonce.', 'location-weather' ) );
    }
    // ... (logic to delete transients via $wpdb query)
}

---

// includes/Blocks/Blocks.php L154
wp_localize_script(
    'spl_weather_editor_js',
    'splWeatherBlockLocalize',
    array(
        'ajaxUrl'          => admin_url( 'admin-ajax.php' ),
        'blockApiNonce'    => wp_create_nonce( 'splw_block_api_nonce' ),
        'pluginUrl'        => LOCATION_WEATHER_URL,
        'blockOptions'     => get_option( 'splw_blocks_visibility_options' ),
        'weather_api_info' => $weather_api_info,
    )
);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.2/includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.3/includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php
--- /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.2/includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php	2026-04-07 06:54:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.3/includes/Admin/AdminDashboard/Splw_Blocks_Page_Wrapper.php	2026-05-07 10:45:58.000000000 +0000
@@ -253,6 +253,9 @@
 	 * Handle AJAX request to update block settings.
 	 */
 	public function splw_update_block_options() {
+		// Check user capabilities, current_user_can() is called internally.
+		location_weather_verify_capability();
+
 		$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
 		if ( ! wp_verify_nonce( $nonce, 'splw_admin_settings_nonce' ) ) {
 			wp_send_json_error( __( 'Invalid nonce.', 'location-weather' ) );
@@ -328,6 +329,9 @@
 	 * It performs nonce verification to ensure the request is legitimate.
 	 */
 	public function lwp_clean_weather_transients() {
+		// Check user capabilities, current_user_can() is called internally.
+		location_weather_verify_capability();
+
 		$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
 		if ( ! wp_verify_nonce( $nonce, 'splw_admin_settings_nonce' ) ) {
 			wp_send_json_error( __( 'Invalid nonce.', 'location-weather' ) );
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.2/includes/functions.php /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.3/includes/functions.php
--- /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.2/includes/functions.php	2025-09-06 06:30:28.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/location-weather/3.0.3/includes/functions.php	2026-05-07 10:45:58.000000000 +0000
@@ -12,8 +12,18 @@
 /**
  * Plugin dashboard access capability.
  *
- * @return manage_options
+ * @return string
  */
 function location_weather_dashboard_capability() {
 	return apply_filters( 'location_weather_access_capability', 'manage_options' );
 }
+
+/**
+ * Verifies the current user has the required capability for admin AJAX actions.
+ * Sends a 403 JSON error and halts execution if the check fails.
+ */
+function location_weather_verify_capability() {
+	if ( ! current_user_can( location_weather_dashboard_capability() ) ) {
+		wp_send_json_error( __( 'Unauthorized access.', 'location-weather' ), 403 );
+	}
+}

Exploit Outline

The exploit requires an authenticated user with at least Contributor-level access. 1. Login to the WordPress admin panel and navigate to a page that initializes the Gutenberg block editor (e.g., /wp-admin/post-new.php). 2. Extract the nonce from the global JavaScript object `splWeatherBlockLocalize.blockApiNonce`, which is enqueued for all users who can access the block editor. 3. Send a POST request to `/wp-admin/admin-ajax.php` with the action `splw_update_block_options`, the extracted nonce, and an `options` array parameter containing weather block visibility flags set to `false`. This disables the weather blocks site-wide. 4. Alternatively, send a POST request with the action `lwp_clean_weather_transients` and the nonce to flush the plugin's weather data cache.

Check if your site is affected.

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