Login
ChallengesLearn
Scoreboard
Teams
SPNZ

LearnSQL InjectionSQL Injection: Error-based extraction
SQL Injection·Lesson 7 of 20

SQL Injection: Error-based extraction

The database wants to tell you the answer - let it. CAST, EXTRACTVALUE, and the verbose error messages that print schema and data right onto the page.

Intermediate14 min
SQLiErrorsExfiltration
Loading lesson…
PreviousSQL Injection: UNION-based extractionNextSQL Injection: Blind injection overview

© 2026 SPNZ.

Terms of ServicePrivacy PolicyCookie Policy

Sometimes the database wants to tell you the answer - let it. Verbose error messages in development and staging environments are one of the most efficient ways to extract data, because the database does the work of fetching it and you only have to read the response.

Prerequisites
Read these lessons first:
  • L1Fundamentals
  • L3Why it's possible
What you'll be able to do
  • Trigger database errors that surface the value of a subquery.
  • Use CAST, EXTRACTVALUE, UPDATEXML, and Oracle's CTXSYS family.
  • Recognise when an application is too verbose in production to be safe.
  • Know why error-based extraction only works when error messages are visible to the attacker.
Key terms
Verbose error
A database error message that includes the offending value, column, table, or query. The default in many development environments; usually suppressed in production.
CAST failure
Forcing a value to the wrong type. The database fails and prints the original value in the error - turning a runtime error into a data exfiltration channel.
XPATH error
EXTRACTVALUE and UPDATEXML expect a valid XPATH. Passing a concatenated string makes MySQL surface the string in the XPATH error message.
Side channel
An information leak that isn't the intended response. Errors are the most common side channel in SQLi; timing is another (covered later).
What is it?

When the database tells you what you asked

Most databases include the offending value in the error message when a query fails. The simplest exfiltration is to put the data you want inside an expression that is guaranteed to fail.

sqlvulnerable
-- MySQL: trying to cast a string version() to INT fails
-- and surfaces the version string in the error.
SELECT * FROM products WHERE id = 1 AND 1=CAST((SELECT version()) AS INT);

-- PostgreSQL: same trick, same result
SELECT * FROM products WHERE id = 1 AND 1=CAST((SELECT current_user) AS INTEGER);

This works in development. In production, error messages are usually swallowed and replaced with a generic 500. The next three lessons cover the techniques that work even when the application is silent. But when error messages are visible - which they almost always are in staging, dev, and many SaaS UIs - error-based extraction is faster than anything else.

Information leak through the error channel
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

Five database primitives

Cycle through the templates. The query panel shows the payload the application builds; the response panel shows what the database returns. Five templates cover the four major engines - MySQL, PostgreSQL, and Oracle.

prodacme-portal.app/api/products?id=1
acme-portal
Pick a database primitive

MySQL refuses to cast a string version ("8.0.32-0ubuntu0.20.04.2") to an integer and surfaces the value in the error.

Injected query
SELECT * FROM products WHERE id = ' AND 1=CAST((SELECT version()) AS INT)-- 
Press Execute to see the database error response. The error contains the data we asked for.
Real-world relevance

When production leaks anyway

Even in production, error messages leak through:

  • Sentry and log dashboards. The application returns a generic 500 to the user, but the developer sees the full stack trace - including any value the attacker coerced into the query.
  • Error-tracking webhooks.Tools like Sentry, Datadog, and Bugsnag forward error events to third-party services. A well-timed attack can exfiltrate data into the third-party's log search.
  • Verbose 500 pages. Many frameworks (Django in DEBUG=True, Rails, Laravel in development) print the failing query in the HTML body of the 500 response.
Mitigation

What kills error-based extraction

Two changes matter. First, the application must never reflect database error messages in the response. Catch the exception, log it server-side, return a generic 500. Second - and this is the important one - parameterise the query. A bound parameter cannot become a CAST argument because the parser never sees it as SQL.

javascriptparameterised
// SAFE - bound parameter; the value never reaches the parser as code
const query = 'SELECT * FROM products WHERE id = $1';
await db.query(query, [id]);
Further reading
  • Error-based SQL injection (PortSwigger)(PortSwigger)
  • MySQL EXTRACTVALUE / UPDATEXML reference(MySQL)
  • PostgreSQL error reporting(PostgreSQL)
Key takeaways

What to remember

  • Error-based extraction works when the database includes the offending value in its error message.
  • The four canonical primitives are CAST (any DB), EXTRACTVALUE and UPDATEXML (MySQL), and the CTXSYS family (Oracle).
  • Production is not always safe - log dashboards, Sentry, and verbose framework defaults all leak.
  • The real fix is the same as lesson 1: parameterise. The error channel becomes a generic 500 with no useful payload.

Knowledge check

0/3 answered · 0 correct
  1. 1.Why does CAST((SELECT version()) AS INT) leak the version string?

  2. 2.You trigger a CAST error in production. The user sees a generic 500, but Sentry captures the full error. Is error-based extraction still possible?

  3. 3.Which MySQL primitives are commonly used for error-based extraction?