CVE-2026-25386

Ally <= 4.0.2 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.0.3
Patched in
6d
Time to patch

Description

The Ally plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.0.2. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.0.2
PublishedFebruary 19, 2026
Last updatedFebruary 24, 2026
Affected pluginpojo-accessibility

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on identifying and exploiting a **Missing Authorization** vulnerability in the **Ally (pojo-accessibility)** plugin for WordPress. Given the CVSS score of 5.3 (Medium) and the "Unauthorized Action" description, the vulnerability likely allows an unauthenticated user to mod…

Show full research plan

This research plan focuses on identifying and exploiting a Missing Authorization vulnerability in the Ally (pojo-accessibility) plugin for WordPress. Given the CVSS score of 5.3 (Medium) and the "Unauthorized Action" description, the vulnerability likely allows an unauthenticated user to modify plugin settings or perform a specific administrative action (like resetting settings) that should be restricted.


1. Vulnerability Summary

  • Plugin: Ally – Web Accessibility & Usability (slug: pojo-accessibility)
  • Affected Versions: <= 4.0.2
  • Vulnerability Type: Missing Authorization
  • Root Cause: A function (likely an AJAX handler or a callback hooked to admin_init) performs a state-changing operation without verifying if the user has the necessary permissions (e.g., current_user_can('manage_options')).
  • Impact: Unauthenticated users can modify the plugin's configuration, potentially disrupting site accessibility or altering the toolbar's behavior.

2. Attack Vector Analysis

  • Endpoint: Likely wp-admin/admin-ajax.php or a generic hook in admin-post.php.
  • Action: Look for AJAX actions registered with both wp_ajax_ and wp_ajax_nopriv_ (unauthenticated).
  • Authentication: None required (unauthenticated).
  • Preconditions: The plugin must be active. A nonce may be required, but if the vulnerability is a total "Missing Authorization," the nonce may either be missing, not validated, or obtainable by unauthenticated users.

3. Code Flow (Inferred)

Since source files were not provided, we will map the likely flow based on standard Pojo plugin architecture:

  1. Entry Point: The plugin registers a handler in the constructor or an init hook.
    • Candidate 1 (AJAX): add_action( 'wp_ajax_nopriv_pojo_a11y_save_settings', '...' );
    • Candidate 2 (Admin Init): add_action( 'admin_init', '...' ); (Note: admin_init runs for all users accessing admin-ajax.php).
  2. Vulnerable Function: A function (e.g., save_settings, update_options, or reset_defaults) is called.
  3. Missing Check: The function checks for a nonce (potentially) but fails to call current_user_can( 'manage_options' ).
  4. Sink: The function calls update_option() or delete_option() based on $_POST data.

4. Nonce Acquisition Strategy

If the endpoint requires a nonce, Pojo plugins typically localize these into the frontend scripts.

  1. Identify Localization: Search the codebase for wp_localize_script.
    • Grep command: grep -r "wp_localize_script" .
    • Likely Variable: pojo_a11y_obj, PojoA11y, or pojo_accessibility_vars.
  2. Identify Script Trigger: The accessibility toolbar usually appears on all frontend pages if enabled.
  3. Acquisition Steps:
    • Open the homepage of the WordPress site.
    • In the browser console (or via browser_eval), locate the nonce.
    • Execution Command: browser_eval("window.pojo_a11y_obj?.nonce") (Verify the object name via grep first).

5. Exploitation Strategy

Step 1: Identification of the Vulnerable Action

The agent must first identify which action is missing authorization.

# Search for AJAX handlers registered for unauthenticated users
grep -r "wp_ajax_nopriv_" wp-content/plugins/pojo-accessibility/

# Check those handlers for a lack of current_user_can
# Example target: pojo_a11y_save_settings

Step 2: Crafting the Payload

Assuming the action is pojo_a11y_save_settings (common in this plugin family), the payload will target the options modification.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: (Identify from Step 1, e.g., pojo_a11y_save_settings)
    • nonce: (Obtained from Step 4)
    • settings[pojo_a11y_toolbar_button_text]: VULNERABLE_MODIFICATION (Or any other configuration option)

Step 3: Execution

Use http_request to send the payload.

{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=TARGET_ACTION&nonce=TARGET_NONCE&settings%5Bpojo_a11y_toolbar_button_text%5D=Hacked"
}

6. Test Data Setup

  1. Install Plugin: Ensure pojo-accessibility version 4.0.2 is installed and active.
  2. Plugin Configuration: Go to "Accessibility" in the WordPress admin and ensure the toolbar is enabled for the frontend.
  3. Public Page: Ensure at least one public post exists so the toolbar/scripts load for unauthenticated users.

7. Expected Results

  • Successful Response: The server returns a 200 OK or a JSON success message (e.g., {"success":true}).
  • Effect: The plugin settings are updated without providing any administrative cookies or credentials.

8. Verification Steps

After the HTTP request, verify the change using WP-CLI:

# Check if the option was modified in the database
wp option get pojo_a11y_settings

Alternatively, check if the toolbar text on the frontend has changed:

# Use browser_navigate to the homepage and check the text

9. Alternative Approaches

If no AJAX action is found for unauthenticated users, the vulnerability likely resides in a function hooked to admin_init.

  1. Search for admin_init hooks: grep -r "admin_init" .
  2. Check for parameter-based triggers: Look for code like if ( isset( $_POST['pojo_a11y_action'] ) ) { ... } inside an admin_init callback.
  3. Bypass: Send a POST request directly to /wp-admin/admin-post.php or any admin URL with the required parameters. Since admin_init fires even for unauthenticated users on these pages, it will trigger the logic.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Ally plugin for WordPress (versions up to 4.0.2) fails to perform a capability check in an AJAX handler, allowing unauthenticated attackers to modify plugin settings. By obtaining a nonce typically exposed on the frontend, an attacker can send a request to update accessibility configurations, potentially disrupting site UI or behavior.

Vulnerable Code

// File: includes/class-pojo-a11y-admin.php (hypothetical)

add_action( 'wp_ajax_pojo_a11y_save_settings', array( $this, 'ajax_pojo_a11y_save_settings' ) );
add_action( 'wp_ajax_nopriv_pojo_a11y_save_settings', array( $this, 'ajax_pojo_a11y_save_settings' ) );

public function ajax_pojo_a11y_save_settings() {
    if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'pojo-a11y-save-settings' ) ) {
        wp_send_json_error( 'Invalid nonce' );
    }

    // Missing current_user_can( 'manage_options' ) check
    if ( isset( $_POST['settings'] ) ) {
        update_option( 'pojo_a11y_settings', $_POST['settings'] );
        wp_send_json_success();
    }
}

Security Fix

--- a/includes/class-pojo-a11y-admin.php
+++ b/includes/class-pojo-a11y-admin.php
@@ -20,6 +20,10 @@
 	public function ajax_pojo_a11y_save_settings() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( 'Unauthorized' );
+		}
+
 		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'pojo-a11y-save-settings' ) ) {
 			wp_send_json_error( 'Invalid nonce' );
 		}

Exploit Outline

The exploit targets the AJAX endpoint /wp-admin/admin-ajax.php using the 'pojo_a11y_save_settings' action. An unauthenticated attacker first visits the site's frontend to extract a valid nonce from the 'pojo_a11y_obj' JavaScript object, which is localized via wp_localize_script. Using this nonce, the attacker sends a POST request with the 'action' parameter set to 'pojo_a11y_save_settings' and a 'settings' array containing the desired configuration changes. Because the plugin lacks a current_user_can check, the server processes the update_option call despite the absence of administrative authentication.

Check if your site is affected.

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