Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/pentesting-web/sql-injection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,32 @@ Payload (URL-encoded): `%27%20OR%20%271%27%3D%271` → decoded: `' OR '1'='1`
JSON_VALUE(metadata, '$.department') = '' OR '1'='1'
```

### Structured query-builder / raw-expression injection

Parameterized values do not help when attacker input is interpreted as part of a **query-builder AST**. If an API field expected to be a scalar reaches the builder without strict type and shape validation, a JSON object or array can select a builder directive instead of becoming bound data. For example, HoneySQL's `:raw` expression renders its argument as literal SQL, while `:lift` is intended to keep a sequence or map as a parameter value.<sup>[[14]](#references)[[16]](#references)</sup>

During testing, focus on the **data-to-query-syntax boundary**, not only quote characters. Replace scalar identifiers with objects/arrays, add undocumented keys, repeat the test across JSON and form encodings, and compare errors, timing, row counts, and generated-SQL traces. Classic string payloads may miss this class because the attacker injects a valid AST node rather than escaping from a quoted literal.<sup>[[14]](#references)[[16]](#references)</sup>

CVE-2026-72898 is an example: affected Metabase password-reset handling accepted an unexpected structured `user-id` value that became a HoneySQL raw expression. The following is only the request shape—the exact SQL expression and generated query are adapter/version-specific:<sup>[[13]](#references)[[16]](#references)</sup>

```http
POST /api/session/reset_password HTTP/1.1
Host: metabase.example:3000
Content-Type: application/json

{
"token": "<reset-token>",
"user-id": {"raw": "<SQL expression>"},
"password": "<new-password>"
}
```

When the sink is the application's own database, prioritize authentication state such as user IDs, password hashes/reset tokens, role or superuser flags, sessions, and API keys. Turning database write capability into an application administrator session can then expose legitimate query/export features and stored connection secrets; access to connected data sources inherits the privileges of the application's configured service accounts.<sup>[[13]](#references)[[15]](#references)[[16]](#references)</sup>

For Metabase incident triage, the vendor identifies `POST /api/session/reset_password` returning `400` followed shortly by `GET /api/user/current` returning `200` as a likely-compromise sequence. Correlate it with object-valued `user-id` bodies, unexplained administrator or `core_user.is_superuser` changes, new API keys, session activity, and subsequent queries against connected databases.<sup>[[15]](#references)[[16]](#references)</sup>

Hardening must happen before query construction: reject unknown fields and non-primitive identifier values, rebuild permitted query nodes server-side from an allow-list, and never deserialize a request object directly into a builder DSL. Keep `:raw` expressions limited to static trusted application code; use bound values (or HoneySQL `:lift` when a map/sequence is genuinely database data) for attacker-influenced values.<sup>[[14]](#references)[[16]](#references)</sup>

### ORDER BY / identifier-based SQLi (PDO limitation)

Prepared statements **cannot bind identifiers** (column or table names). A common unsafe pattern is to take a user-controlled `sort` parameter and build `ORDER BY` using string concatenation, sometimes wrapping the input in backticks to “sanitize” it. This still enables SQLi because the identifier context is attacker-controlled.<sup>[[12]](#references)</sup>
Expand Down Expand Up @@ -721,5 +747,9 @@ https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
- [10] [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
- [11] [CVE-2026-22730: SQL Injection in Spring AI's MariaDB Vector Store](https://blog.securelayer7.net/cve-2026-22730-sql-injection-spring-ai-mariadb/)
- [12] [HTB: Gavel](https://0xdf.gitlab.io/2026/03/14/htb-gavel.html)
- [13] [Metabase advisory GHSA-vwf4-m7j8-wcjf](https://github.com/metabase/metabase/security/advisories/GHSA-vwf4-m7j8-wcjf)
- [14] [HoneySQL special syntax: `raw` and `lift`](https://github.com/seancorfield/honeysql/blob/develop/doc/special-syntax.md)
- [15] [Metabase security update and attack pattern](https://www.metabase.com/blog/security-update)
- [16] [CVE-2026-72898: Critical Metabase Unauthenticated SQL Injection Vulnerability](https://offsec.com/blog/cve-2026-72898-2)

{{#include ../../banners/hacktricks-training.md}}