Merge pull request #1439 from HackTricks-wiki/update_ReDisclosure__New_technique_for_exploiting_Full-Te_20250925_184639

ReDisclosure New technique for exploiting Full-Text Search i...
This commit is contained in:
SirBroccoli 2025-10-01 02:38:03 +02:00 committed by GitHub
commit 5a81e0b2d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -198,6 +198,55 @@ mysql> select @@version;
mysql> select version();
```
## MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR)
This is not a classic SQL injection. When developers pass user input into `MATCH(col) AGAINST('...' IN BOOLEAN MODE)`, MySQL executes a rich set of Boolean search operators inside the quoted string. Many WAF/SAST rules only focus on quote breaking and miss this surface.
Key points:
- Operators are evaluated inside the quotes: `+` (must include), `-` (must not include), `*` (trailing wildcard), `"..."` (exact phrase), `()` (grouping), `<`/`>`/`~` (weights). See MySQL docs.
- This allows presence/absence and prefix tests without breaking out of the string literal, e.g. `AGAINST('+admin*' IN BOOLEAN MODE)` to check for any term starting with `admin`.
- Useful to build oracles such as “does any row contain a term with prefix X?” and to enumerate hidden strings via prefix expansion.
Example query built by the backend:
```sql
SELECT tid, firstpost
FROM threads
WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE);
```
If the application returns different responses depending on whether the result set is empty (e.g., redirect vs. error message), that behavior becomes a Boolean oracle that can be used to enumerate private data such as hidden/deleted titles.
Sanitizer bypass patterns (generic):
- Boundary-trim preserving wildcard: if the backend trims 12 trailing characters per word via a regex like `(\b.{1,2})(\s)|(\b.{1,2}$)`, submit `prefix*ZZ`. The cleaner trims the `ZZ` but leaves the `*`, so `prefix*` survives.
- Early-break stripping: if the code strips operators per word but stops processing when it finds any token with length ≥ min length, send two tokens: the first is a junk token that meets the length threshold, the second carries the operator payload. For example: `&&&&& +jack*ZZ` → after cleaning: `+&&&&& +jack*`.
Payload template (URL-encoded):
```
keywords=%26%26%26%26%26+%2B{FUZZ}*xD
```
- `%26` is `&`, `%2B` is `+`. The trailing `xD` (or any two letters) is trimmed by the cleaner, preserving `{FUZZ}*`.
- Treat a redirect as “match” and an error page as “no match”. Dont auto-follow redirects to keep the oracle observable.
Enumeration workflow:
1) Start with `{FUZZ} = a…z,0…9` to find first-letter matches via `+a*`, `+b*`, …
2) For each positive prefix, branch: `a* → aa* / ab* / …`. Repeat to recover the whole string.
3) Distribute requests (proxies, multiple accounts) if the app enforces flood control.
Why titles often leak while contents dont:
- Some apps apply visibility checks only after a preliminary MATCH on titles/subjects. If control-flow depends on the “any results?” outcome before filtering, existence leaks occur.
Mitigations:
- If you dont need Boolean logic, use `IN NATURAL LANGUAGE MODE` or treat user input as a literal (escape/quote disables operators in other modes).
- If Boolean mode is required, strip or neutralize all Boolean operators (`+ - * " ( ) < > ~`) for every token (no early breaks) after tokenization.
- Apply visibility/authorization filters before MATCH, or unify responses (constant timing/status) when the result set is empty vs. non-empty.
- Review analogous features in other DBMS: PostgreSQL `to_tsquery`/`websearch_to_tsquery`, SQL Server/Oracle/Db2 `CONTAINS` also parse operators inside quoted arguments.
Notes:
- Prepared statements do not protect against semantic abuse of `REGEXP` or search operators. An input like `.*` remains a permissive regex even inside a quoted `REGEXP '.*'`. Use allow-lists or explicit guards.
## Other MYSQL injection guides
- [PayloadsAllTheThings MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
@ -206,9 +255,10 @@ mysql> select version();
- [PayloadsAllTheThings MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
- [Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/)
- [MySQL Full-Text Search Boolean mode](https://dev.mysql.com/doc/refman/8.4/en/fulltext-boolean.html)
- [MySQL Full-Text Search Overview](https://dev.mysql.com/doc/refman/8.4/en/fulltext-search.html)
- [MySQL REGEXP documentation](https://dev.mysql.com/doc/refman/8.4/en/regexp.html)
- [ReDisclosure: New technique for exploiting Full-Text Search in MySQL (myBB case study)](https://exploit.az/posts/wor/)
{{#include ../../../banners/hacktricks-training.md}}