
This repository contains a practical research and validation toolkit for CVE-2025-4396, an unauthenticated Time-Based Blind SQL Injection affecting the WordPress Relevanssi plugin through the `cats` parameter.
This repository contains a practical research and validation toolkit for CVE-2025-4396, an unauthenticated Time-Based Blind SQL Injection affecting the WordPress Relevanssi plugin through the cats parameter.
The project was built for authorized Purple Team engagements in order to:
The repository includes:
Disclaimer
This project is provided for educational, defensive validation, and authorized security testing only.
Do not use it against systems you do not own or do not have explicit written permission to assess.
CVE-2025-4396 is a SQL Injection vulnerability affecting the Relevanssi search functionality in WordPress.
In the tested scenario, the issue is reachable through the search workflow and more specifically through the cats parameter. The vulnerable code path allows attacker-controlled input to influence the SQL query generated by the plugin.
Because the vulnerable endpoint is accessible without prior authentication, the flaw can be exploited by a remote attacker to perform unauthenticated SQL injection.
The practical impact includes:
The vulnerability exists because user-controlled input from a search-related parameter is not safely handled before being incorporated into a SQL query.
In practice, this means an attacker can inject SQL expressions into the backend query logic and force the database to evaluate additional conditions.
The vulnerability is exploited in blind mode, which means the application does not directly display SQL errors or raw database results.
Instead of reading query output from the page, the attacker asks the database a series of true/false questions and observes one side effect:
The exploitation relies on SQL functions such as SLEEP() to create a measurable difference in server response time.
This allows an attacker to infer data without ever seeing it directly.
For example, the attacker can ask questions such as:
$?”By repeating this process, the attacker can reconstruct a full hash character by character.
The standard approach iterates through a known character set and tests each candidate one by one.
For each position in the target hash:
This method is simple and reliable, but relatively slow because it may require many requests per character.
The faster approach uses binary search on the ASCII value of each character.
Instead of asking:
a?”b?”c?”it asks:
This divides the search space in half on every request and reduces the number of HTTP requests dramatically.
The attack allows the operator to extract the user_pass value from wp_users, typically for a chosen WordPress user ID such as:
1 for the default administrator,In recent WordPress versions, this value may use the new WordPress 6.8+ password pipeline, which combines:
CVE_2025_4396.pyThis is the standard extraction script.
It performs a classic Time-Based Blind SQL Injection and extracts the target hash character by character using a linear search over a fixed charset.
For each character position:
python3requestsurllib3pip3 install requests urllib3
python3 CVE_2025_4396.py -t "https://target.local/?s=test&cats=" -u 1 -s 3 -v
Arguments :
-t, --target : vulnerable target URL including the injectable parameter
-u, --userid : WordPress user ID to target
-s, --sleep : sleep threshold in seconds
-v, --verbose : enable debug logging
CVE_2025_4396_Stealth.pyThis is the binary search edition.
It performs the same extraction objective as the standard script, but replaces the linear character-by-character search with a binary search on ASCII values.
For each position:
python3requestsurllib3pip3 install requests urllib3
python3 CVE_2025_4396_Stealth.py -t "https://target.local/?s=test&cats=" -u 1 -s 3 -v
Once the password hash has been extracted from WordPress, the next step is to crack it offline.
WordPress 6.8+ Hashing Notes. In the tested workflow, the extracted value may look like:
$wp$2y$10$zPA8xGJMvr.kvAAIIYaCreIMnTDpgw/9o8K7.ONBm/KBbNIywQtu.
For Hashcat, the usable bcrypt portion is:
$2y$10$zPA8xGJMvr.kvAAIIYaCreIMnTDpgw/9o8K7.ONBm/KBbNIywQtu.
However, this bcrypt is not applied directly to the raw password. WordPress first applies a preprocessing stage:
This means a normal wordlist cannot be sent directly to Hashcat if you want to reproduce the exact WordPress 6.8+ logic.
Instead, each candidate password must be transformed first into its WordPress-compatible pre-hash representation.
The repository also includes a "helper" script that:
$wp$ prefix when needed,Extract the hash :
$wp$2y$10$zPA8xGJMvr.kvAAIIYaCreIMnTDpgw/9o8K7.ONBm/KBbNIywQtu.Prepare the cracking environment :
python3hashcat and available in PATHRun Auto_Crack.py :
python3 Auto_Crack.py -H '\$wp\$2y\$10\$zPA8xGJMvr.kvAAIIYaCreIMnTDpgw/9o8K7.ONBm/KBbNIywQtu.' -w /usr/share/wordlists/rockyou.txt
We need to escape "$" with "\" -> bash compatibility
The following detection content can be used by SOC teams to identify exploitation attempts and measure defensive maturity.
title: Potential Time-Based Blind SQLi (CVE-2025-4396 Relevanssi)
id: 5a8a1c93-5c74-4b5b-a620-8e1c3e41ab5d
status: experimental
description: Detects HTTP GET requests containing typical Time-Based Blind SQL injection payloads often used to exploit CVE-2025-4396 in the WordPress Relevanssi plugin (bypassing comma filters).
author: n3fhara
date: 2026-03-18
tags:
- attack.initial_access
- attack.t1190
- cve.2025-4396
logsource:
category: webserver
detection:
selection_endpoint:
cs-uri-query|contains:
- 's='
- 'cats='
- 'tags='
selection_payload:
cs-uri-query|contains:
- 'SLEEP('
- 'WAITFOR'
- 'SUBSTRING('
- 'ASCII('
- 'LENGTH('
selection_bypass_indicators:
cs-uri-query|contains:
- 'FROM'
- 'FOR 1'
- '*('
condition: selection_endpoint and selection_payload and selection_bypass_indicators
falsepositives:
- Highly unlikely. Legitimate search queries should not contain SQL functions.
level: high
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"ET EXPLOIT WordPress Relevanssi SQLi Attempt (CVE-2025-4396)"; flow:established,to_server; content:"GET"; http_method; content:"cats="; http_uri; pcre:"/(cats|tags)=.*(SLEEP|WAITFOR)%28.*(%2A|\*).*SUBSTRING/i"; classtype:web-application-attack; sid:1000001; rev:1; metadata:created_at 2026_03_18, cve CVE_2025_4396;)
index=web_logs sourcetype=access_combined
| regex uri_query="(?i)cats=|tags="
| stats count as request_count, avg(response_time) as avg_time, max(response_time) as max_time, dc(uri_query) as unique_payloads by clientip
| where request_count > 20 AND max_time > 2000
| sort - max_time
url.query : (*cats=* OR *tags=* OR *s=*) AND url.query : (*SLEEP* OR *WAITFOR* OR *SUBSTRING* OR *ASCII*) AND url.query : (*FROM* OR *FOR* OR *%2A*)
sequence by source.ip with maxspan=1m
[network where url.path == "/" and url.query : "*cats=*" and event.duration > 2000000000]
[network where url.path == "/" and url.query : "*cats=*" and event.duration > 2000000000]
[network where url.path == "/" and url.query : "*cats=*" and event.duration > 2000000000]
The baseline detections above are good for non-obfuscated exploitation, but they become weaker when an operator introduces more advanced tradecraft.
SOC visibility degrades when the attacker starts using:
.
├── CVE_2025_4396.py
├── CVE_2025_4396_Stealth.py
├── Auto_Crack.py
├── README.md
└── relevanssi.4.24.4.zip
Use only in environments where you are explicitly authorized to test. The authors and contributors assume no liability for misuse.
Disclaimer
This project is provided for educational, defensive validation, and authorized security testing only.
Do not use it against systems you do not own or do not have explicit written permission to assess.