SQL injection: from a broken quote to the whole database
SQLi challenges reward a systematic climb: confirm the injection, work out the query shape, pull data with UNION, and fall back to boolean and time oracles when the output is hidden. Plus the auth-bypass one-liners and how to beat basic filters.
SQL injection happens when user input is concatenated into a query instead of being passed as a parameter, so your input stops being data and becomes part of the SQL. It is one of the oldest web bugs and still one of the most common CTF web categories, because it rewards method: there is a clear ladder from 'I think this is injectable' to 'here is the entire users table', and each rung has a standard technique.
The mistake beginners make is throwing ' OR 1=1-- at everything and giving up when it does not visibly work - including at backends that are not SQL at all, where the same idea has different syntax. The climb below is more reliable, and it tells you at each step what to try next.
Step 1: confirm, and learn the context
First establish that input reaches the query, and how it is quoted. A single quote is the classic probe: if ' causes an error or a change in behaviour, something is parsing it as SQL. Then confirm it is really injection and not just an error, by breaking and then fixing the query.
' -- does it error? that's a syntax break = promising
' -- -- comment out the rest; does the error go away?
' OR '1'='1 -- always-true; does it return more rows?
1' AND '1'='2 -- always-false; does it return fewer/none?Step 2: auth bypass, the free win
If the injection is in a login form, you may not need to extract anything. A query like SELECT * FROM users WHERE user='$u' AND pass='$p' becomes an unconditional match when you close the string and comment out the password check.
Username: admin'--
Username: admin' OR '1'='1
Username: ' OR 1=1 LIMIT 1--Step 3: UNION, when you can see output
If the query's results are displayed, UNION SELECT appends a second result set of your choosing - so you can select from any table you like. Two things must line up first: the number of columns, and which of them is actually printed.
-- Find the column count: increase until the error stops.
' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- ...
-- Or probe directly with NULLs until it succeeds:
' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- ...
-- Find which column is visible, then read the schema:
' UNION SELECT 1,2,3-- -- which number appears?
' UNION SELECT NULL,table_name,NULL FROM information_schema.tables--
' UNION SELECT NULL,username,password FROM users--Step 4: blind SQLi, when you cannot
Often nothing from the query is displayed - only whether the page 'worked' or not, or how long it took. That is still enough. Blind SQLi extracts data one bit at a time by asking yes/no questions and reading the answer from the page's behaviour.
- Boolean-based. The page renders differently for true and false.
' AND (SELECT SUBSTRING(password,1,1) FROM users LIMIT 1)='a'--- if the page looks 'true', the first character is 'a'. Walk each position, each candidate character. - Time-based. Nothing visibly changes, but you can make the database pause.
' AND IF((...)='a', SLEEP(3), 0)--- a three-second delay means the condition was true. Slower, but works when there is no other signal at all. - Error-based. The app leaks database errors. Force the answer into an error message with a function like
extractvalueorupdatexmland read the data out of the error text.
Beating simple filters
Many challenges filter keywords or characters. The bypasses are well-worn because the filters are shallow.
| Filter | Bypass |
|---|---|
| Blocks spaces | Comments as whitespace: '/**/OR/**/1=1--, or %0a, %09, parentheses |
Blocks OR/AND (case-sensitive) | Change case (oR), or use ||/&& |
Blocks UNION SELECT | UNI/**/ON SEL/**/ECT, or nested/case tricks |
| Strips a keyword once | Nest it: SELSELECTECT - stripping the inner leaves SELECT |
| Blocks quotes | Use numeric context, or CHAR(97)/hex 0x61 to build strings |
| WAF on comments | -- -, #, or terminate with a real trailing clause instead of commenting |
When to just run sqlmap
sqlmap automates every step above - detection, column counting, schema enumeration, and all three blind techniques with binary search built in. In a CTF it is the right tool once you have confirmed injection by hand and understand the shape, because it will grind out a blind extraction far faster and more reliably than you will. sqlmap -u '...' -p param --dump is often the whole endgame. Confirm manually so you know it is real and where; then let the tool do the tedious extraction.
The ladder
- Probe with a quote; confirm with the AND-true/AND-false pair and note the quote context.
- On a login, try admin'-- and OR 1=1 before anything else.
- If output is visible: ORDER BY for column count, then UNION SELECT from information_schema.
- If output is hidden: boolean, time, or error-based extraction - with binary search.
- Filtered? Match the bypass to the filter; treat the filter as the hint.
- Confirmed and understood? Hand the grind to sqlmap.