PT0-002Chapter 18 of 104Objective 3.2

SQL Injection: Union, Blind, Time-Based

This chapter covers SQL injection (SQLi) techniques, specifically Union-based, Blind (Boolean-based), and Time-based SQL injection, which are critical for the CompTIA PenTest+ PT0-002 exam. These techniques fall under Domain 3: Attacks and Exploits, Objective 3.2: Exploit network-based vulnerabilities. Approximately 10-15% of exam questions touch on SQL injection in some form, making it a high-yield topic. Mastering these methods will enable you to identify, exploit, and recommend remediation for SQL injection vulnerabilities in web applications.

25 min read
Intermediate
Updated May 31, 2026

SQL Injection: The Hotel Keycard Analogy

Imagine a hotel where each room's door lock accepts a numeric code typed on a keypad. The hotel management system processes codes by concatenating user input into a database query: "SELECT * FROM rooms WHERE code = '" + user_input + "';". A guest named Bob enters his code "1234". The system runs: SELECT * FROM rooms WHERE code = '1234'; and unlocks room 101. Now, a malicious guest types: ' OR 1=1 --. The system runs: SELECT * FROM rooms WHERE code = '' OR 1=1 --'; The double dash comments out the trailing quote, and OR 1=1 makes the condition always true. The system returns all rooms, effectively unlocking every door. This is a classic SQL injection. In a blind SQL injection scenario, the attacker doesn't see the query result directly but can infer information by observing whether the door unlocks or not (true/false). By asking questions like "Does the code start with '1'?" (by injecting ' OR code LIKE '1%' --), the attacker can guess each digit. Time-based blind injection is similar but uses a delay: if the door takes 5 seconds to respond, the condition was true; if it responds instantly, false. The attacker uses SQL's SLEEP() function to introduce delays based on a condition, allowing data extraction without any visible output.

How It Actually Works

What is SQL Injection and Why Does It Exist?

SQL injection (SQLi) is a code injection technique that exploits a security vulnerability occurring in the database layer of an application. The vulnerability is present when user input is either incorrectly filtered for string literal escape characters embedded in SQL statements or user input is not strongly typed and thereby unexpectedly executed. It is one of the most common web application vulnerabilities, consistently ranking in the OWASP Top 10.

The root cause is the failure to separate data from commands. When an application constructs SQL queries by concatenating user-supplied input directly into the query string, an attacker can inject malicious SQL code that alters the query's intended logic. For example, a login form might execute:

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

If the application takes the username and password fields and concatenates them without sanitization, an attacker can input ' OR '1'='1 as both fields, resulting in:

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

This query returns all rows because '1'='1' is always true, bypassing authentication.

How SQL Injection Works Internally

SQL injection exploits the syntax and structure of SQL. The attacker's goal is to break out of the intended data context and inject SQL keywords. The process typically involves:

1.

Identifying injectable parameters: The attacker tests input fields (e.g., search boxes, login forms, URL parameters) with special characters like a single quote (') to see if it causes an error or unexpected behavior.

2.

Determining the database type: Error messages often reveal the database (MySQL, MSSQL, Oracle, etc.), which affects the syntax of injection payloads.

3.

Crafting a payload: The attacker constructs a string that, when concatenated, completes the original query and adds their own SQL commands.

4.

Extracting data: Depending on the technique (Union, Blind, Time-based), the attacker retrieves information from the database.

Union-Based SQL Injection

Union-based SQL injection leverages the SQL UNION operator to combine the results of the original query with results from an attacker-controlled query. The key requirement is that the number of columns and data types in both queries must match.

Step-by-step mechanism:

1. Determine the number of columns: Use ORDER BY or UNION SELECT NULL to find the column count. For example:

' ORDER BY 1 --
   ' ORDER BY 2 --
   ... until an error occurs.

Or:

' UNION SELECT NULL --
   ' UNION SELECT NULL,NULL --
   ...

The number of NULLs that works without error indicates the column count.

2. Find the output columns: Determine which columns are displayed on the page. Inject:

' UNION SELECT 'a','b','c' --

Look for the letters 'a', 'b', 'c' in the output. Those columns are usable for data extraction.

3. Extract data: Replace the letters with database functions or subqueries. For MySQL:

' UNION SELECT database(), user(), version() --

This returns the current database name, database user, and version.

Limitations: Union-based injection requires the application to display the results of the query. If the application does not output the data (e.g., only returns a success/failure message), blind techniques are needed.

Blind SQL Injection (Boolean-Based)

Blind SQL injection occurs when the application does not display the results of the injected query but still behaves differently based on whether the query returns true or false. The attacker can infer information by asking yes/no questions.

How it works: The attacker injects a condition that causes the application to respond differently (e.g., different page content, HTTP status code, or response time). For example:

' OR (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') = 'a' --

If the first character of the admin password is 'a', the condition is true, and the page loads normally. If false, the page might show an error or no results.

Common techniques: - Substring extraction: Use SUBSTRING() or MID() to extract one character at a time. - Binary search: Instead of testing each character individually, use comparisons like ASCII(SUBSTRING(...)) > 64 to narrow down the character set faster. - Conditional errors: Some databases allow conditional errors (e.g., IF(condition, (SELECT 1/0), 'false')). If the condition is true, the division by zero causes an error; if false, no error.

Limitations: Boolean-based blind injection is slow because each character requires many requests. Automation tools like sqlmap are often used.

Time-Based Blind SQL Injection

Time-based blind SQL injection is used when the application does not provide any visible difference between true and false responses. The attacker uses database functions that cause a delay (e.g., SLEEP(), WAITFOR DELAY) to infer the truth of a condition.

Mechanism: The attacker injects a conditional delay:

' OR IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') = 'a', SLEEP(5), 0) --

If the condition is true, the database sleeps for 5 seconds before responding. The attacker observes the response time. If the page takes ~5 seconds longer than normal, the condition is true; otherwise, false.

Database-specific functions: - MySQL: SLEEP(seconds) - MSSQL: WAITFOR DELAY '0:0:5' - Oracle: DBMS_LOCK.SLEEP(5) (requires privileges) or DBMS_PIPE.RECEIVE_MESSAGE('pipe', 5) - PostgreSQL: pg_sleep(5)

Limitations: Time-based injection is even slower than Boolean-based because each request takes several seconds. Network latency must be accounted for; a 5-second delay might be indistinguishable from a 4-second delay if the baseline is unstable.

Key Components, Values, Defaults, and Timers

Column count determination: Use ORDER BY with incrementing numbers until error. If 3 columns work but 4 fails, the table has 3 columns.

NULL usage: In UNION SELECT, NULL is used because it is compatible with any data type.

Comment sequences:

- MySQL: -- (note space after double dash) or # - MSSQL: -- - Oracle: -- - String concatenation:

- MySQL: CONCAT('a','b') or 'a' 'b' (space concatenation) - MSSQL: 'a'+'b' - Oracle: 'a'||'b' - Database version functions:

- MySQL: @@version or VERSION() - MSSQL: @@VERSION - Oracle: SELECT banner FROM v$version - Current database:

- MySQL: DATABASE() - MSSQL: DB_NAME() - Oracle: SYS_CONTEXT('USERENV','DB_NAME') - Current user:

- MySQL: USER() or CURRENT_USER() - MSSQL: SYSTEM_USER or CURRENT_USER - Oracle: USER or SYS_CONTEXT('USERENV','SESSION_USER')

Configuration and Verification Commands

Manual testing with curl:

curl 'http://example.com/page?id=1'
curl 'http://example.com/page?id=1''  # Check for error
curl 'http://example.com/page?id=1' OR '1'='1'  # Check for true condition

Using sqlmap:

sqlmap -u 'http://example.com/page?id=1' --batch
sqlmap -u 'http://example.com/page?id=1' --dbs
sqlmap -u 'http://example.com/page?id=1' -D database --tables
sqlmap -u 'http://example.com/page?id=1' -D database -T users --dump

Verification of vulnerability: If injecting ' OR '1'='1 returns more results than expected, the parameter is vulnerable.

How SQL Injection Interacts with Related Technologies

Web Application Firewalls (WAF): WAFs can detect and block SQLi payloads. Attackers use evasion techniques like case variation, URL encoding, comments (/**/), or using OR 1=1 inside /*!*/ (MySQL inline comments).

Prepared Statements (Parameterized Queries): These are the primary defense. They separate SQL logic from data, making injection impossible. The application defines the query structure first, then passes parameters separately.

Stored Procedures: If not written securely, stored procedures can still be vulnerable to SQL injection if they concatenate input.

ORM (Object-Relational Mapping): Frameworks like Hibernate or Entity Framework can reduce injection risk but are not immune if raw queries are used.

NoSQL Databases: NoSQL injection exists (e.g., MongoDB), but the syntax differs (e.g., '$ne': null).

Walk-Through

1

Identify Injectable Parameters

Begin by mapping the application's attack surface. Identify all input fields that interact with a database: URL parameters (GET), form fields (POST), cookies, and HTTP headers. Test each parameter with a single quote (`'`) to see if it causes a database error or unexpected behavior. For example, change `?id=1` to `?id=1'` and observe the response. If an error message is displayed (e.g., 'You have an error in your SQL syntax'), the parameter is likely injectable. If no error appears, try other characters like double quote (`"`), backslash (`\`), or parentheses. Also test with `OR 1=1` to see if the response changes (e.g., more rows returned). Record which parameters are vulnerable.

2

Determine Database Type and Version

Once injection is confirmed, identify the database management system (DBMS) to craft appropriate payloads. Use database-specific functions in injected queries. For MySQL, inject `' UNION SELECT @@version --` to get the version. For MSSQL, use `' UNION SELECT @@VERSION --`. For Oracle, use `' UNION SELECT banner FROM v$version --`. Error messages often reveal the DBMS (e.g., 'MySQL' or 'Microsoft OLE DB'). The version helps determine available functions (e.g., `SLEEP()` in MySQL 5+). If no error messages, use blind techniques: test for `SLEEP(5)`; if the response delays, it's MySQL or PostgreSQL. Test `WAITFOR DELAY '0:0:5'` for MSSQL.

3

Determine Number of Columns (Union)

For Union-based injection, you must match the column count of the original query. Use `ORDER BY` with incrementing numbers: inject `' ORDER BY 1 --`, then `' ORDER BY 2 --`, etc., until an error occurs. The last successful number is the column count. Alternatively, use `UNION SELECT NULL`: inject `' UNION SELECT NULL --`, then `' UNION SELECT NULL,NULL --`, etc. The number of NULLs that returns a normal page (no error) is the column count. For example, if `' UNION SELECT NULL,NULL,NULL --` works but `' UNION SELECT NULL,NULL,NULL,NULL --` fails, there are 3 columns.

4

Find Output Columns (Union)

After determining the column count, identify which columns are displayed on the page. Inject a query that places unique strings in each column, e.g., `' UNION SELECT 'a','b','c' --` for a 3-column table. Look for the strings 'a', 'b', 'c' in the response. The columns where these strings appear are usable for data extraction. If none appear, try different positions or use NULL for non-displayed columns. Also note that some columns may be used for internal processing (e.g., IDs) and not shown. Only columns that output data are useful.

5

Extract Data (Union, Blind, or Time-Based)

With output columns identified, extract sensitive data. For Union-based, replace the test strings with database functions or subqueries: `' UNION SELECT database(), user(), version() --`. For Boolean-based blind, use conditional statements with `SUBSTRING()` and observe true/false responses: `' OR (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' --`. For Time-based blind, inject conditional delays: `' OR IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a', SLEEP(5), 0) --`. Automate with sqlmap for efficiency. Extract table names, column names, and data rows.

What This Looks Like on the Job

In a real-world penetration test, SQL injection vulnerabilities are often found in legacy applications or custom-built web interfaces that lack proper input validation. One common scenario is an e-commerce website with a product search feature. The search parameter is passed directly into a SQL query: SELECT * FROM products WHERE name LIKE '%' + userInput + '%'. An attacker can inject ' UNION SELECT creditCardNumber, expiry, cvv FROM payments -- to retrieve payment data. In production, this vulnerability could lead to massive data breaches, as seen in the 2017 Equifax breach where a SQL injection in a dispute portal exposed 147 million records. Another scenario is a corporate intranet login page. The authentication query might be SELECT * FROM users WHERE username='$user' AND password='$pass'. An attacker can bypass authentication by entering admin' -- as the username, effectively commenting out the password check. In penetration tests, we often find that developers use stored procedures thinking they are safe, but if the stored procedure concatenates input internally, it remains vulnerable. For example, a stored procedure that builds a dynamic query with EXEC() is still injectable. Performance considerations: Union-based injection on a large database can cause heavy load, potentially crashing the application. Time-based injection is slow; a single character extraction might take minutes. Automation tools like sqlmap handle this but can be noisy and trigger intrusion detection systems. Misconfigurations often occur when developers enable detailed error messages in production, giving attackers a roadmap. The best defense is parameterized queries (prepared statements) with strict input validation. In my experience, many organizations also fail to apply the principle of least privilege to database accounts; the application often connects with full read/write access, allowing data extraction or even data modification via injection.

How PT0-002 Actually Tests This

The PT0-002 exam tests SQL injection under Objective 3.2 (Exploit network-based vulnerabilities) and also touches on it in 3.3 (Exploit application-based vulnerabilities). Expect multiple-choice questions where you must identify the type of injection based on a description or code snippet. Common wrong answers: (1) Choosing 'Cross-Site Scripting' when the description involves database queries. Remember: XSS injects client-side scripts, not SQL. (2) Confusing 'Blind SQL injection' with 'Error-based SQL injection'. Error-based uses database error messages to extract data; blind does not show errors, only true/false differences. (3) Thinking that 'Time-based' is the same as 'Blind' — it is a subtype of blind, but the exam distinguishes them. (4) Selecting 'Stored Procedure Injection' when the vulnerability is due to dynamic SQL in a stored procedure, but the root cause is still SQL injection. Key values: The exam may ask for the correct comment syntax for MySQL (-- with space), or the function to delay in MSSQL (WAITFOR DELAY). Be familiar with the UNION syntax and the requirement that column counts match. Edge cases: The exam loves to test that UNION SELECT requires the same number of columns; if the original query has 3 columns, UNION SELECT 1,2 will fail. Also, note that ORDER BY can be used to find column count but may not work if the query uses DISTINCT or GROUP BY. Another edge case: In Oracle, every SELECT must have a FROM clause, so you must use FROM dual in UNION injections. To eliminate wrong answers, focus on the mechanism: if the response includes data from the database, it's likely Union-based; if only true/false changes, it's Boolean blind; if time delays, it's Time-based. Always check if the vulnerability involves concatenation of user input into a SQL query — that is the hallmark of SQLi.

Key Takeaways

SQL injection occurs when user input is concatenated into SQL queries without sanitization.

Union-based injection requires matching the number of columns in the original query using ORDER BY or UNION SELECT NULL.

Blind SQL injection uses true/false responses to infer data character by character.

Time-based blind injection uses database delay functions (e.g., SLEEP(), WAITFOR DELAY) to infer conditions.

The comment syntax for MySQL is '-- ' (double dash followed by space) or '#'.

Prepared statements with parameterized queries are the definitive defense against SQL injection.

sqlmap is a powerful automated tool for detecting and exploiting SQL injection vulnerabilities.

On the PT0-002 exam, identify the injection type by the behavior: data output (Union), true/false differences (Boolean blind), or time delays (Time-based).

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

Union-Based SQL Injection

Requires the application to display query results.

Returns data directly in the response.

Faster extraction (single request per row).

Requires matching column count and data types.

Easier to detect because output changes visibly.

Blind SQL Injection (Boolean-Based)

Works when no data is displayed, only true/false responses.

No direct data output; infer information bit by bit.

Slower; many requests needed per character.

Does not require column count knowledge initially.

Harder to detect; only subtle differences in page content.

Watch Out for These

Mistake

SQL injection only works on Microsoft SQL Server.

Correct

SQL injection can affect any database that uses SQL, including MySQL, Oracle, PostgreSQL, SQLite, and others. The payload syntax varies by database, but the core vulnerability is the same.

Mistake

Using stored procedures automatically prevents SQL injection.

Correct

Stored procedures can still be vulnerable if they use dynamic SQL (e.g., `EXEC()` or `sp_executesql` with concatenated strings). Only parameterized stored procedures are safe.

Mistake

Blind SQL injection is faster than Union-based because it uses less data.

Correct

Blind SQL injection is slower because it requires many requests (one per character) to extract data. Union-based can retrieve multiple rows in a single request.

Mistake

Time-based SQL injection is undetectable because it doesn't change the page content.

Correct

Time-based injection is detectable by monitoring response times. It also leaves a trail of delayed requests in server logs, which can be flagged by intrusion detection systems.

Mistake

Escaping quotes with backslashes is a complete defense against SQL injection.

Correct

Escaping is not foolproof; it can be bypassed in some contexts (e.g., using alternate encodings or database-specific functions). Parameterized queries are the only reliable defense.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

How do I determine the number of columns in a UNION-based SQL injection?

Use the ORDER BY clause with incrementing numbers: inject ' ORDER BY 1 --, then ' ORDER BY 2 --, etc. When you get an error, the previous number is the column count. Alternatively, use UNION SELECT NULL: inject ' UNION SELECT NULL --, then add more NULLs until the page loads normally. The number of NULLs that works is the column count. For example, if ' UNION SELECT NULL,NULL,NULL -- works but ' UNION SELECT NULL,NULL,NULL,NULL -- fails, there are 3 columns.

What is the difference between blind SQL injection and time-based SQL injection?

Blind SQL injection (Boolean-based) relies on differences in the application's response (e.g., page content, HTTP status) based on whether a condition is true or false. Time-based SQL injection is a subtype of blind injection used when no visible difference exists; it uses database delay functions to infer the condition. For example, if the condition is true, the database sleeps for 5 seconds. Both are 'blind' because no data is directly output, but time-based is specifically for scenarios with no observable true/false difference.

Can SQL injection be exploited on a login page without seeing error messages?

Yes, through blind SQL injection. Even if no error messages are shown, the application may respond differently to valid vs. invalid credentials. For example, a successful login might redirect to a dashboard, while a failed login stays on the login page. An attacker can inject a condition like ' OR 1=1 -- to bypass authentication, or use Boolean-based blind to extract password hashes character by character. Time-based blind can also be used if the response time varies based on the condition.

What is the correct syntax for a time-based delay in MySQL?

In MySQL, the correct function is SLEEP(seconds). For example: ' OR SLEEP(5) -- causes a 5-second delay if the condition is true. Note that SLEEP() is a function and must be used in a context where it is executed. Common payload: ' OR IF(condition, SLEEP(5), 0) --. The IF() function evaluates the condition and executes SLEEP(5) if true.

How does sqlmap automate SQL injection detection?

sqlmap is a Python tool that automatically detects and exploits SQL injection vulnerabilities. It works by sending various payloads to the target URL and analyzing responses. It can identify the database type, extract data, and even spawn an interactive shell. Basic usage: sqlmap -u 'http://example.com/page?id=1' --batch. It tests parameters for error-based, union-based, blind, and time-based injection. It also has options to set delays, threads, and evasion techniques.

What is the role of the '--' comment in SQL injection?

The double dash '--' is a comment in SQL that tells the database to ignore the rest of the query. In injection, it is used to remove any trailing SQL syntax (like a closing quote or WHERE clause) that would otherwise cause an error. For example, if the original query is SELECT * FROM users WHERE username = '$input', injecting ' OR 1=1 -- results in SELECT * FROM users WHERE username = '' OR 1=1 --', which comments out the final quote, making the query valid.

Can NoSQL databases be vulnerable to injection?

Yes, NoSQL databases like MongoDB are also vulnerable to injection, but the attack vector is different. Instead of SQL, the injection targets the query language (e.g., MongoDB's JSON-based queries). For example, a login form might pass a JSON object like {username: userInput, password: passInput}. An attacker could inject {username: {$ne: null}, password: {$ne: null}} to bypass authentication. The PT0-002 exam may test this as a form of injection, but SQL injection is more common.

Terms Worth Knowing

Ready to put this to the test?

You've just covered SQL Injection: Union, Blind, Time-Based — now see how well it sticks with free PT0-002 practice questions. Full explanations included, no account needed.

Done with this chapter?