You throw a single quote into a login field. The page reloads exactly the same as before: no stack trace, no ugly database error, no visible change at all. It's tempting to write "not injectable" in your notes and move on to the next parameter. That conclusion is wrong more often than people expect, because a visible error is only one class of SQL injection, and the two kinds that don't announce themselves, boolean-based blind and time-based blind, are exactly the ones a five-second test misses.
Here's the methodology that actually rules them out instead of assuming them away.
Start by testing a true/false pair, not a single payload
A single ' tells you almost nothing. Send two requests instead: one where the injected condition is true (' OR '1'='1) and one where it's false (' OR '1'='2). If the app is vulnerable and the difference isn't a loud error, it will usually still be some difference: a longer response, a different redirect, a login that succeeds versus one that doesn't, a slightly different word count in the page. That difference is your oracle. Without it, "no error" and "not vulnerable" are not the same statement, and treating them as the same is the actual mistake.
Boolean-based blind: read the page, not the error log
Once you have a working true/false oracle, you can extract data one bit at a time. Ask the database conditional questions through the query itself: is the first character of the admin password's hash greater than 'm'? Is the length of the current database name equal to 6? Each answer shows up as the same visible difference you found in the true/false test. It's slow, but it's completely mechanical, and it's the technique that gets dismissed as "well the page didn't error" more than any other.
Time-based blind: when there's no visible difference at all
Some apps genuinely look identical no matter what you send. That's when you stop asking the database to show you something and start asking it to take its time: AND IF(1=1, SLEEP(5), 0). A true condition costs you five real seconds of response time; a false one comes back immediately. No content difference required, because the timing itself is the signal.
The catch is confirming it's real and not noise. Run the false-condition version a few times as a baseline first, use a delay long enough that network jitter can't explain it (5 seconds, not 1), and always test both branches, true and false, before you trust the result. A single slow response proves nothing on its own; a consistent, reproducible gap between the two branches does.
Where SQLMap actually fits
SQLMap isn't magic and it isn't cheating, it's automating exactly the oracle-testing loop above across every injection point and DBMS-specific payload variant, faster and more thoroughly than doing it by hand. That's also why understanding the manual methodology matters even when you plan to reach for it: its --technique=B and --technique=T flags map directly to boolean-based and time-based blind, rate limiting or a WAF can make it report "not injectable" on a target that actually is, and knowing the underlying oracle test is what lets you catch that and re-verify by hand instead of trusting a false negative.
The gap between "no visible error" and "confirmed blind injection with a working extraction path" is exactly what Codelivly's SQL Injection Notes walks through payload by payload, boolean and time-based both, alongside the payloads that actually get past filtering. If you want to drill the oracle-building step by hand first, the SQLi login bypass lab and the NimbusPay SQLi CTF challenge are free and built around this exact test.
Top comments (0)