Ally, formerly One Click Accessibility, is a popular accessibility toolkit by Elementor with over 400,000 active installations. It adds skip-to-content links, font resizing, contrast adjustments, and other Web Content Accessibility Guidelines (WCAG) features. Versions up to and including 4.0.3 contain an unauthenticated SQL injection vulnerability (CVE-2026-2413, CVSS 7.5) in the plugin’s Remediation module.
An attacker can extract sensitive data from the WordPress database without logging in.
The fix landed in version 4.1.0 on 23 February 2026.
What the vulnerability does
The Remediation module tracks page-level accessibility fixes and stores them in custom database tables. On every frontend page load, it calls Utils::get_current_page_url() to work out which page the visitor is on, then looks up any active remediations for that URL.
The page URL comes from $_SERVER['REQUEST_URI']. The developer ran it through esc_url_raw() before using it, which is the right function if you’re sanitising a URL for storage or a redirect. But it’s the wrong function if you’re about to put that value into an SQL query. esc_url_raw() permits single quotes, parentheses, and other characters that are perfectly valid in URLs but break SQL string literals wide open.
The tainted URL ends up in get_global_remediations() inside remediation-entry.php, where it’s appended to a JOIN clause:
$join = "LEFT JOIN $excluded_table
ON $remediation_table.id = $excluded_table.remediation_id
AND $excluded_table.page_url = '$url'";No $wpdb->prepare(), no escaping for SQL context. The single quotes around $url are the only defence – a single quote in the URL itself breaks straight out of them. Classic SQL injection!
Something odd though… the WHERE conditions in the same query use the plugin’s structured array format, which does flow through $wpdb->prepare(). The JOIN parameter, however, is accepted as a raw string. The abstraction layer handles one safely and leaves the other wide open. It’s the kind of inconsistency that is easy to miss in review.
Because the query results control remediation behaviour rather than page content, an attacker can’t read data directly from the HTTP response. The practical technique is time-based blind injection: craft a URL path containing a SLEEP() call conditional on a database value, measure the response time, and extract data one character at a time. Automated tools like sqlmap handle this efficiently.
The impact is full read access to the WordPress database. That means user credentials (password hashes), email addresses, session tokens, and any data stored by other plugins. On a WooCommerce site, it means order history, customer addresses, payment metadata, API keys to external services, etc.
There is one meaningful constraint. The Remediation module only loads when the plugin is connected to an Elementor account. Sites running Ally without that connection are not vulnerable, which narrows the exposed population. But 400,000 active installs is a large number, and a significant number of those will have the module active.
How to check
# Check the plugin's version. wp plugin list --name=pojo-accessibility --field=version
If the result is 4.0.3 or lower, update immediately.
What to do
Update to 4.1.0 or later. The patch replaces the string interpolation with $wpdb->prepare():
$join = $wpdb->prepare(
"LEFT JOIN $excluded_table
ON $remediation_table.id = $excluded_table.remediation_id
AND $excluded_table.page_url = %s",
$url
);That is the correct fix. $wpdb->prepare() with %s handles escaping for MySQL string context, which is exactly what esc_url_raw() does not do.
If you cannot update immediately, deactivating the plugin removes the risk entirely. Alternatively, disconnecting the Elementor account will prevent the Remediation module from loading, which closes the vulnerable code path without losing the plugin’s core accessibility features.