eWPTX study materials
eWPTX study resources

SQLi Behind a WAF That Only Blocks the Obvious

eWPTX · Put your knowledge to work in context.

All case studies

SQLi Behind a WAF That Only Blocks the Obvious

A PHP/MySQL 8.0 login form sits behind a cloud WAF. Sending ' OR 1=1-- - returns a generic 403 from the WAF. Sending ' alone returns the app's own error page with a stack trace showing mysqli_query() and the query SELECT id FROM users WHERE email = '$e'. Sending ' AND 1=1-- - returns 'invalid credentials'; ' AND 1=2-- - also returns 'invalid credentials' - identical bodies and identical response times within noise. The WAF appears to inspect the raw query string only. You have a valid low-priv account for the app's authenticated area but the login form is pre-auth. The client forbids time-based payloads that could load the DB.

1. Given the identical responses to 1=1 and 1=2, what is your next diagnostic move, and why is boolean inference unreliable here?

Read the worked answer

The identical bodies don't prove the injection is dead - they prove the login logic short-circuits. A failed mysqli_query returns false, and the app likely treats 'no row' and 'query error' the same way, so both branches collapse to 'invalid credentials.' Before concluding anything, I'd establish which branch I'm in:

  • Send ' AND (SELECT 1 FROM DUAL)-- - vs a syntactically invalid payload like ' AND (-- -. If the error page fires for the invalid one but not the valid one, the query is executing and the injection is live.
  • Check the stack trace: it may echo the faulted query, which lets me read my own injected text back and confirm parsing.

Boolean inference is unreliable because the observed channel (login success) is downstream of both a query failure and a genuine false condition - they're indistinguishable. I need a channel that separates 'query ran and returned false' from 'query errored,' e.g. the error page itself, a different status code, or a length difference in the error body.

2. The WAF blocks on raw-string patterns. Describe how you would build an extraction channel that stays under it without time-based payloads, and how you would validate it before trusting it.

Read the worked answer

Use the error page as an oracle rather than boolean inference. MySQL lets me force a conditional error: ' AND (SELECT 1 FROM (SELECT IF(ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>64,1,1/0))x)-- -. When the condition is true, 1/0 raises a division-by-zero, the query errors, and the stack trace page appears; when false, the query returns a row and the app shows 'invalid credentials.' That's a clean two-state channel the WAF's raw-string regex won't catch if I vary whitespace/comments and avoid the literal keywords it's keyed on.

Validate before trusting: run the payload with a condition I know is true (1=1) and one I know is false (1=2) and confirm the two distinct responses. Then run a known-true condition twice to confirm stability. Only after the oracle is stable do I start extraction - otherwise I'll be decoding noise. If the error page is suppressed in production, fall back to a response-length difference on the same conditional, still without time delays. Note in the report that the WAF is bypassable by pattern variation, since that's a finding in itself.