On this page 31 sections
- Understand SQL before SQL injection
- Connect to MySQL
- Navigate databases
- Explore tables and columns
- Read data with SELECT
- Filter results
- Sort and limit output
- Boolean logic matters
- What SQL injection changes
- Testing input boundaries
- Authentication bypass concept
- Why comments are useful
- UNION-based SQL injection
- Find the number of columns
- Confirm the column count with UNION
- Identify visible columns
- Fingerprint MySQL
- Identify the current database
- INFORMATION_SCHEMA
- Enumerate databases
- Enumerate tables
- Enumerate columns
- Retrieve data from another database
- Identify the current MySQL user
- Inspect database privileges
- Understand secure_file_priv
- Reading local files
- Writing files from MySQL
- Think in stages
- How to prevent SQL injection
- Key takeaway
Understand SQL before SQL injection
SQL injection becomes much easier to understand when the underlying SQL syntax is clear. A web application usually builds queries to read or modify data. If user-controlled input is inserted into those queries without proper parameterisation, that input may change the structure or logic of the SQL statement.
Connect to MySQL
The MySQL client can connect directly to a database server using a username, host and port. The -p option asks for the password interactively.
mysql -u root -h db.sam0x.me -P 3306 -pNavigate databases
A MySQL server can contain multiple databases. SHOW DATABASES lists them, while USE changes the active database for subsequent queries.
SHOW DATABASES;USE users;Explore tables and columns
Once a database is selected, SHOW TABLES reveals the tables available in that database. DESCRIBE shows the structure of a table, including its columns and data types.
SHOW TABLES;DESCRIBE logins;Read data with SELECT
SELECT retrieves data from a table. The asterisk returns every column, while specifying column names limits the output to the fields that are actually needed.
SELECT * FROM logins;SELECT username, password FROM logins;Filter results
WHERE restricts the rows returned by a query. LIKE performs pattern matching, and the percent character acts as a wildcard in MySQL.
SELECT * FROM logins WHERE id = 1;SELECT * FROM logins WHERE username LIKE 'admin%';Sort and limit output
ORDER BY controls the order of returned rows. ASC sorts ascending and DESC descending. LIMIT restricts how many rows MySQL returns and can also specify an offset.
SELECT * FROM logins ORDER BY id ASC;SELECT * FROM logins ORDER BY id DESC;SELECT * FROM logins LIMIT 2;SELECT * FROM logins LIMIT 1, 2;Boolean logic matters
SQL conditions are evaluated using operators such as =, !=, LIKE, NOT, AND and OR. Understanding operator precedence is important because an injected OR condition can change whether the WHERE clause evaluates to true or false.
SELECT * FROM logins WHERE username = 'sam0x' AND active = 1;What SQL injection changes
Consider an application that builds a query by directly concatenating a username supplied by the user. If that value contains SQL syntax, the resulting statement may no longer represent the query the developer intended. SQL injection is therefore not simply inserting strange characters: it is changing the structure or logic of the database query.
SELECT * FROM users WHERE username = '$username' AND password = '$password';Testing input boundaries
One of the first signs of SQL injection can appear when a quote changes the application response or produces a database error. Quotes matter because applications frequently place user input inside SQL string literals.
'Authentication bypass concept
If user input becomes part of an authentication query, a boolean expression that always evaluates to true may alter the WHERE condition. SQL comments can also remove the remainder of the original query. The exact payload depends on the surrounding SQL syntax.
admin' OR '1'='1admin')-- -Why comments are useful
After injecting valid SQL syntax, the rest of the application query may still contain characters that cause an error. MySQL comments can cause the remaining portion of the original query to be ignored. The -- comment syntax normally requires whitespace after the two hyphens, which is why examples often use -- -.
-- -UNION-based SQL injection
UNION combines the result of one SELECT statement with another. For UNION injection to work, both SELECT statements must return a compatible number of columns, and corresponding values need compatible data types.
Find the number of columns
Before constructing a UNION query, determine how many columns the original SELECT returns. ORDER BY can be incremented until the application produces an error, indicating that the requested column position no longer exists.
' ORDER BY 1-- -' ORDER BY 2-- -' ORDER BY 3-- -Confirm the column count with UNION
Another technique is to submit numbered values through UNION SELECT. If the number of supplied values does not match the original query, MySQL returns an error. Once the column count matches, the UNION query can execute.
cn' UNION SELECT 1,2,3-- -cn' UNION SELECT 1,2,3,4-- -Identify visible columns
Numbered UNION values can also reveal which columns are rendered back into the HTTP response. If the page displays one of the numbers, that position can later be replaced with database information.
cn' UNION SELECT 1,2,3,4-- -Fingerprint MySQL
@@version returns the MySQL server version when query results are visible. SLEEP() can produce a controlled delay and is useful for understanding situations where the application does not directly display database output.
SELECT @@version;cn' UNION SELECT 1,@@version,3,4-- -SELECT SLEEP(5);Identify the current database
The database() function returns the database currently selected by the application connection.
cn' UNION SELECT 1,database(),2,3-- -INFORMATION_SCHEMA
MySQL exposes metadata through INFORMATION_SCHEMA. Instead of guessing database, table and column names, this metadata can describe the structure of the server itself.
Enumerate databases
INFORMATION_SCHEMA.SCHEMATA contains database names. The schema_name column can therefore be queried to list databases visible to the current MySQL account.
cn' UNION SELECT 1,schema_name,3,4 FROM INFORMATION_SCHEMA.SCHEMATA-- -Enumerate tables
INFORMATION_SCHEMA.TABLES contains table metadata. Filtering by table_schema limits the output to a specific database.
cn' UNION SELECT 1,TABLE_NAME,TABLE_SCHEMA,4 FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='dev'-- -Enumerate columns
After identifying an interesting table, INFORMATION_SCHEMA.COLUMNS can reveal its column names. This provides the structure needed before querying the table itself.
cn' UNION SELECT 1,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='credentials'-- -Retrieve data from another database
MySQL supports fully qualified table names in the form database.table. Once the database, table and columns are known, a query can reference them directly.
cn' UNION SELECT 1,username,password,4 FROM dev.credentials-- -Identify the current MySQL user
The user() function shows the account associated with the current database connection. This is important because the account permissions determine what the application can access.
cn' UNION SELECT 1,user(),3,4-- -Inspect database privileges
The impact of SQL injection depends heavily on database permissions. INFORMATION_SCHEMA.USER_PRIVILEGES can show privileges available to database accounts, while mysql.user may expose administrative attributes when the current user has permission to read it.
cn' UNION SELECT 1,grantee,privilege_type,is_grantable FROM information_schema.user_privileges WHERE grantee="'root'@'localhost'"-- -cn' UNION SELECT 1,super_priv,3,4 FROM mysql.user WHERE user='root'-- -Understand secure_file_priv
MySQL can restrict file import and export operations through secure_file_priv. Depending on its configuration and the database account permissions, file operations may be restricted to one directory or disabled.
cn' UNION SELECT 1,variable_name,variable_value,4 FROM information_schema.global_variables WHERE variable_name='secure_file_priv'-- -Reading local files
MySQL LOAD_FILE() can read a file that the database process can access when the account has the necessary privileges and the server configuration allows it. This is why database accounts should have the minimum privileges required by the application.
cn' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3,4-- -Writing files from MySQL
SELECT ... INTO OUTFILE can create a file on the database server when MySQL permissions, operating-system permissions and secure_file_priv allow it. This significantly increases the impact of a SQL injection vulnerability and should normally be prevented by least-privilege database configuration.
SELECT 'sam0x SQLi lab' INTO OUTFILE '/var/www/html/proof.txt';Think in stages
A structured SQL injection assessment is easier to understand than trying random payloads. First determine whether input affects SQL syntax, then identify the query structure, determine the column count, locate visible output, fingerprint the DBMS and enumerate metadata only as far as necessary for the lab objective.
'' ORDER BY 1-- -cn' UNION SELECT 1,2,3,4-- -cn' UNION SELECT 1,database(),2,3-- -How to prevent SQL injection
The primary defence is to keep user data separate from SQL syntax by using parameterised queries or prepared statements. Applications should also validate input, avoid exposing detailed database errors, run with a least-privilege database account and avoid granting unnecessary FILE or administrative privileges.
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");\n$stmt->execute([$username]);Key takeaway
SQL injection is fundamentally a query-construction problem. Learning SQL syntax, boolean logic and database metadata makes the vulnerability much easier to understand. UNION injection is only one technique; the real objective is to determine how user input influences the query and what privileges the application database account actually has.