CVE-2026-15941
Executive Summary
CVE-2026-15941 is a time-based blind SQL injection vulnerability in the Relevanssi WordPress search plugin's Admin Search AJAX handler. An authenticated attacker with only edit_posts capability (contributor-level) can inject arbitrary SQL through a URL-encoded args parameter, leading to full database exfiltration without requiring any special privileges. The vulnerability carries a CVSS score of 6.5 (Medium severity), reflecting the low complexity and network exploitability, though the impact is limited to the database's confidentiality. At the time of this report, no official vendor patch exists, and the plugin remains exposed in active WordPress installations.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability resides in the Relevanssi plugin's Admin Search functionality, which is exposed via an AJAX endpoint accessible from the WordPress dashboard. The plugin authenticates users based on the edit_posts capability, meaning any user with this permission—including subscriber-level accounts that have been granted posting rights—can access the affected handler.
Vulnerability Mechanism:
Parameter Handling: The AJAX handler receives a POST request containing a URL-encoded
argsparameter. This parameter is expected to contain serialized query arguments that will be parsed into aWP_Queryobject.Parsing Logic: The plugin calls
parse_str()or similar PHP functions to decode theargsparameter, then constructs aWP_Queryinstance with the resulting array. This array can include arbitrary WordPress query variables, includingtax_queryparameters.Taxonomy Restriction Builder: When a
tax_queryis present, Relevanssi passes the taxonomy data to its internal taxonomy restriction builder. This component is responsible for generating SQL fragments that restrict search results based on taxonomy terms.SQL Injection Point: The taxonomy value is sanitized using WordPress's
sanitize_text_field()function, which strips HTML tags and line breaks. However, this sanitization does not parameterize the value for SQL. The plugin then interpolates the sanitized value directly into a SQL query string that queries thewp_term_taxonomytable:
SELECT term_taxonomy_id FROM wp_term_taxonomy
WHERE term_id = 'VALUE_HERE' AND taxonomy = 'VALUE_HERE'
Since both values are concatenated into the query without using $wpdb->prepare() with proper placeholders, an attacker can inject SQL by crafting values that break out of the single-quoted string context.
- Blind Exploitation: The query is wrapped in a conditional that affects response timing. By injecting a
SLEEP()function or a heavy subquery, the attacker can measure response delays to infer whether their injected conditions are true, enabling time-based blind extraction of database contents.
Attack Surface Details:
- Endpoint: WordPress admin-ajax.php (action hook specific to Relevanssi)
- Authentication Required: Yes —
edit_postscapability only - Network Vector: Remote, HTTP POST
- SQL Database: MySQL/MariaDB (standard WordPress installations)
- Interpolated Query:
wp_term_taxonomytable, where the attacker controls both theterm_idandtaxonomyvalues
Exploitation Scenarios
The following attack chain demonstrates a concrete exploitation path for CVE-2026-15941:
Step 1 — Reconnaissance and Target Identification
The attacker enumerates WordPress sites running the Relevanssi plugin. Using Shodan dorks like http.component:"WordPress" http.body:"relevanssi" or by fingerprinting Relevanssi's distinctive HTML comments and script handles in page source, the attacker narrows the target pool. Alternatively, the attacker probes a known WordPress site for the plugin by checking for the Relevanssi admin scripts in /wp-admin/load-scripts.php or querying the plugin's data via REST API endpoints.
Step 2 — Attacker Privilege Establishment
The attacker requires a WordPress account with edit_posts capability. Common acquisition vectors include:
- Registering a contributor account if the site allows open registration
- Compromising an existing low-privilege account through credential stuffing or phishing
- Purchasing access from initial-access brokers
A contributor account suffices—no admin or editor privileges are needed.
Step 3 — Crafting the Malicious Request
The attacker constructs an AJAX POST request to the vulnerable endpoint. The args parameter contains a serialized WordPress query array with a tax_query sub-array designed to inject SQL.
A crafted payload targeting the args parameter might look like:
args=tax_query[0][taxonomy]=term&tax_query[0][terms]=1') OR (SELECT(IF(MID((SELECT user_pass FROM wp_users LIMIT 1),1,1)='$',SLEEP(5),false)))-- -
The server-side logic decodes this as:
tax_query[0][taxonomy] = "term"
tax_query[0][terms] = "1') OR (SELECT(IF(MID((SELECT user_pass FROM wp_users LIMIT 1),1,1)='$',SLEEP(5),false)))-- -"
After sanitization (which removes HTML tags but preserves SQL metacharacters), the term_id value becomes:
1') OR (SELECT(IF(MID((SELECT user_pass FROM wp_users LIMIT 1),1,1)='$',SLEEP(5),false)))-- -
Step 4 — SQL Query Construction and Execution
The plugin builds the following vulnerable query:
SELECT term_taxonomy_id FROM wp_term_taxonomy
WHERE term_id = '1') OR (SELECT(IF(MID((SELECT user_pass FROM wp_users LIMIT 1),1,1)='$',SLEEP(5),false)))-- -'
AND taxonomy = 'term'
The injected )-- - terminates the WHERE clause and comments out the rest. The OR (SELECT(...SLEEP(5)...)) clause evaluates to true if the first character of the first user's password hash is $ (a valid prefix for phpass-hashed passwords), causing a 5-second delay in the response. Otherwise, the query returns instantly.
Step 5 — Time-Based Blind Data Extraction
The attacker iterates through characters and positions:
MID((SELECT user_pass FROM wp_users LIMIT 1),1,1)→ guesses character at position 1MID((SELECT user_pass FROM wp_users LIMIT 1),2,1)→ for position 2 of the hash- Same pattern applied to
user_login,user_email, and other table columns
Each request retrieves one character, and a standard brute-force over an alphanumeric-plus-symbol charset extracts a full password hash in approximately 62 requests per character position. Timing analysis distinguishes a successful guess (5-second delay) from an unsuccessful one (immediate response).
Step 6 — Post-Exploitation Escalation
Once the attacker has the admin password hash, they attempt offline cracking using John the Ripper or Hashcat against popular password lists. Because WordPress uses phpass (iterated MD5 with salted keys), weak admin passwords often crack quickly. Success grants full administrative access: site defacement, backdoor plugin installation, server-side code execution via theme/plugin file editing, and lateral movement to the hosting server.
Impact Assessment
Business Impact:
- Confidentiality (C:H): Full database disclosure, including user credentials (password hashes), emails, session tokens, and any custom tables storing sensitive business data. For e-commerce sites, payment transaction metadata may be exposed.
- Integrity (I:N): The vulnerability does not directly allow modification, but the resulting admin compromise enables full site takeover.
- Availability (A:N): No direct denial of service; however, the SLEEP-based payloads can be used to exhaust database connections in a distributed attack.
Affected Systems:
- Any WordPress installation running Relevanssi versions prior to a patch (currently all versions, as unpatched)
- Particularly affected: multisite networks, WooCommerce stores, membership platforms, and content-heavy sites with active user bases
Blast Radius:
- WordPress is deployed on ~43% of all websites; Relevanssi has over 700,000 active installations
- The
edit_postsrequirement lowers the exploitation barrier—any user with contributor role is sufficient - No compatibility constraints: the vulnerability is OS-agnostic and database-agnostic (MySQL-compatible databases only)
CVSS Reference:
The CVSS:3.1 vector AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N scores 6.5. The scope is unchanged (S:U), confirming the database and the WordPress core share the same trust boundary. A legitimate concern: the Medium severity underplays real-world impact because time-based blind injection combined with modern GPU hash cracking yields near-certain admin credential compromise for weakly-padded sites.
Detection Guidance
Sigma Rule
title: Relevanssi Admin Search SQL Injection Attempt
id: c0f1e2a3-b4c5-4d6e-8f90-123456789abc
status: experimental
description: Detects SQL injection patterns in Relevanssi admin search AJAX requests
author: Valtersit Threat Intelligence
date: 2026/01/15
tags:
- attack.t1190
- attack.initial-access
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: POST
cs-uri-query|contains:
- 'admin-ajax.php'
cs-uri-query|contains|all:
- 'action=relevanssi'
request_body|contains:
- "' OR (SELECT"
- 'SLEEP('
- "')-- -"
- 'BENCHMARK('
- "') UNION SELECT"
- "') AND ("
condition: selection
fields:
- clientip
- request_body
falsepositives:
- Legitimate security scanner traffic
- Misconfigured plugin queries with bracket syntax
level: high
YARA Rule
rule Relevanssi_SQLi_CVE_2026_15941
{
meta:
author = "Valtersit Threat Intelligence"
description = "Detects SQL injection patterns targeting Relevanssi Admin Search"
cve = "CVE-2026-15941"
date = "2026-01-15"
severity = "high"
strings:
$ajx = "admin-ajax.php" ascii wide nocase
$act = "relevanssi" ascii wide nocase
$sqli1 = "') OR (SELECT" ascii wide nocase
$sqli2 = "SLEEP(" ascii wide nocase
$sqli3 = "BENCHMARK(" ascii wide nocase
$sqli4 = "' UNION SELECT" ascii wide nocase
$sqli5 = ")-- -" ascii wide nocase
$arg = "args=" ascii wide
$tq_tax = "tax_query" ascii wide nocase
condition:
$ajx and $act and $arg and $tq_tax and 1 of ($sqli*)
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2026-15941 Relevanssi Admin Search SQL Injection Attempt"; flow:established,to_server; content:"POST"; http_method; content:"admin-ajax.php"; http_uri; content:"action=relevanssi"; http_uri; content:"args="; http_client_body; pcre:"/(?:'|\")\s*\)?\s*OR\s+\(?\s*SELECT/i"; pcre:"/SLEEP\s*\(\s*\d+\s*\)/i"; classtype:web-application-attack; reference:cve,2026-15941; sid:202615941; rev:1;)
Elastic Detection
sequence by source.ip with maxspan=30s
[ network where network.protocol == "http" and
http.request.method == "POST" and
http.request.uri contains "admin-ajax.php" and
http.request.body.content contains "action=relevanssi" and
http.request.body.content contains "args=" and
http.request.body.content contains "tax_query" ]
[ network where network.protocol == "http" and
http.response.bytes > 0 and
(http.response.body.content contains "error" or
http.response.body.content contains "failure") ]
Splunk SPL Query
index=web sourcetype=access_combined
| where match(_raw, "admin-ajax") AND match(_raw, "relevanssi") AND match(_raw, "args=")
| rex field=_raw "^(?<clientip>\S+)"
| rex field=_raw "POST (?<uri>[^ ]+)"
| search uri="*admin-ajax.php*"
| rex field=_raw "(?<body>args=[^&]+)"
| eval body=urldecode(body)
| where match(body, "(?i)(\'|\")?\s*OR\s+\(?\s*(SELECT|IF)")
OR match(body, "(?i)SLEEP\s*\(")
OR match(body, "(?i)BENCHMARK\s*\(")
| table _time clientip uri body
| sort - _time
Wazuh OSSEC Rule
<group name="wordpress,">
<rule id="102315" level="12">
<decoded_as>web-access</decoded_as>
<match>admin-ajax.php</match>
<regex>action=relevanssi</regex>
<regex>args=</regex>
<regex>tax_query</regex>
<regex>(?i)\x27\s*OR\s+\(?\s*(SELECT|IF|\x27)</regex>
<description>Relevanssi Admin Search SQL Injection attempt (CVE-2026-15941)</description>
<mitre>
<id>T1190</id>
</mitre>
<group>attack.initial-access,</group>
<options>no_full_log</options>
</rule>
<rule id="102316" level="14">
<if_sid>102315</if_sid>
<regex>(?i)SLEEP\s*\(|BENCHMARK\s*\(|UNION SELECT</regex>
<description>Confirmed time-based blind SQLi payload in Relevanssi Admin Search (CVE-2026-15941)</description>
<group>attack.initial-access,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 0/4 — UNPATCHED — No patch evidence from any tracked source. The vendor has not released a fix as of this report.
1. Immediate Mitigations (until patch is available):
- Restrict
edit_postscapability: Audit all user roles. Removeedit_postsfrom contributor roles if business workflows permit. At minimum, restrict user registration to require manual approval. - Disable Relevanssi Admin Search: Temporarily deactivate the plugin's Admin Search feature by adding a code snippet to a custom mu-plugin or functions.php:
add_action('wp_ajax_relevanssi_admin_search', function() {
wp_die('Admin Search disabled temporarily for security.');
}, 1);
- Web Application Firewall (WAF) Rules: Deploy virtual patching rules that block POST requests to
admin-ajax.phpcontaining bothaction=relevanssiand SQL metacharacters (',--,SLEEP,BENCHMARK,UNION) in theargsparameter. - Database Least Privilege: Ensure the WordPress database user has minimal permissions (SELECT, INSERT, UPDATE, DELETE only—no DROP, GRANT, or FILE).
2. Patch/Upgrade Steps:
- Monitor for updates: Subscribe to the Relevanssi changelog RSS feed and the WordPress Plugin Security Tracker. The moment a patched version is released, upgrade immediately via
wp plugin update relevanssior the WordPress dashboard. - Verify patched version: After the vendor releases a fix, confirm that the code uses
$wpdb->prepare()with%splaceholders for the taxonomy values before reinstating Admin Search.
3. Long-Term Hardening:
- Regular Security Audits: Incorporate this vulnerability class into recurring WordPress plugin vulnerability scans (WPScan, Jetpack Scan).
- Credential Policy: Enforce strong admin password policies. Time-based blind SQLi on password hashes is only dangerous if the hashes crack—mitigate by making admin passwords 16+ characters with high entropy.
- Plugin Reduction: Evaluate whether Relevanssi is essential. For informational sites, native WordPress search may suffice. If retained, keep it updated religiously.
- Web Server Logging: Enable and retain access logs for
admin-ajax.phpfor at least 90 days to facilitate forensic investigations. - Network Segmentation: If the WordPress instance is in a DMZ, ensure the database is isolated on the internal network with strict firewall rules.
References
- NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2026-15941
- WordPress Plugin Repository: https://wordpress.org/plugins/relevanssi/
- Relevanssi Changelog: https://www.relevanssi.com/changelog/
- CWE-89 SQL Injection Reference: https://cwe.mitre.org/data/definitions/89.html
- ATT&CK T1190 Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
Known Threat Actor Campaigns: No active in-the-wild exploitation campaigns have been identified as of this report, and the vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog. However, given the plugin's large installed base and the low privilege requirement, high likelihood of weaponization exists. Security teams should treat this as a priority patching item upon vendor release.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.