You're testing a login form, or a search box, or a product ID parameter sitting right there in the URL. You throw in a single quote and the page throws back a database error instead of "invalid input", something like a MySQL syntax error near '. You clean it up, try ' OR '1'='1, and the response changes: more rows, a different page. Injection confirmed. And then you sit there, because "the parameter is injectable" isn't the deliverable. A finding based on "I made an error message appear" doesn't hold up to a client, a CTF scoreboard, or an interview panel asking what you'd actually do next. The deliverable is data coming out of the database, and that's a different skill than finding the injection point.
The bridge is UNION-based extraction, and it starts with a question that has nothing to do with the data you actually want: how many columns does the original query return? UNION SELECT only works when the column count matches on both sides of the union, so you find it by counting up with ORDER BY 1, ORDER BY 2, ORDER BY 3, and so on until the page errors out or the sort visibly breaks. The last number that worked is your column count. (Or skip straight to UNION SELECT NULL, NULL, NULL -- and keep adding NULLs until it stops erroring, same answer, different route.)
NULL works as a placeholder for any data type, which is exactly why it's the first move, but NULL doesn't tell you anything useful on its own. The next step is figuring out which of those columns the page actually prints back to you. Swap each NULL for a string like 'test' one column at a time; the ones that don't error, and that you can see reflected somewhere on the page, are your output channels. Once you've found one, everything after this is just deciding what to put there instead of 'test'.
That's where information_schema comes in, on MySQL, Postgres and MSSQL alike. UNION SELECT table_name, NULL, NULL FROM information_schema.tables -- lists every table in the database, no guessing required. Find something that looks worth pulling, enumerate its columns the same way, then swap the real table and column names into your working UNION SELECT and the actual rows come back through the reflection point you already found. CONCAT() two or three columns together when you're short on visible output slots and need more than one field back at once.
This whole approach assumes the application echoes query output back onto the page somewhere. A lot of real targets don't: they run the query, do something with the result server-side, and show you nothing on the page itself. That's where UNION-based extraction stops working and you're forced into blind and time-based techniques instead, inferring the data one true/false answer or one time delay at a time rather than reading it directly. It's a slower, more patient version of the same underlying idea, and it's most of what separates a training-lab SQLi from the messier ones you actually run into on an engagement.
The methodology above, column count, type matching, information_schema enumeration, plus the blind and time-based techniques for when nothing reflects, the payload differences between MySQL, MSSQL, Oracle and Postgres, and where SQLMap actually saves you time versus doing it by hand, is what the SQL Injection Notes PDF walks through end to end: https://resources.codelivly.com/product/sql-injection-notes-professional-guide-by-codelivly/
Top comments (0)