Что такое SQL-инъекция с EXTRACTVALUE?
SQL-инъекция — один из самых распространённых способов атаки на веб-приложения, при котором злоумышленник внедряет вредоносный SQL-код в запросы к базе данных. Одной из разновидностей является инъекция, использующая функцию EXTRACTVALUE в СУБД MySQL. Эта функция предназначена для извлечения значения из XML-документа по заданному XPath-выражению. Однако атакующие научились применять её для вызова ошибок, которые содержат полезную информацию, например, имена таблиц, столбцов или даже данные пользователей.
Рассматриваемая строка %"))) AND EXTRACTVALUE(5534,CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e))-- - — это типичный пример такой инъекции. Она состоит из нескольких частей, каждая из которых выполняет свою роль. Давайте разберём её по частям.
Разбор строки инъекции
Начнём с начала: %"))). Это не что иное, как попытка закрыть существующий SQL-запрос, в который внедряется код. Символ % часто используется в LIKE-запросах как подстановочный знак. Далее идут кавычка и несколько закрывающих скобок — они нужны, чтобы «разорвать» оригинальный запрос и добавить свой собственный. Количество скобок подбирается экспериментально, чтобы синтаксис стал корректным.
Затем следует AND EXTRACTVALUE(5534,CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e)). Это и есть полезная нагрузка (payload). Функция EXTRACTVALUE(XML_document, XPath_expression) принимает два аргумента: XML-документ и выражение XPath. Если XPath-выражение некорректно, MySQL генерирует ошибку, в тексте которой отображается переданное выражение. Именно на этом строится атака.
В нашем случае первым аргументом идёт число 5534 — это не XML, но MySQL автоматически преобразует его в строку. Второй аргумент — CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e). Здесь 0x7e — это шестнадцатеричное представление символа ~ (тильда). Функция CONCAT объединяет тильду, результат подзапроса и ещё одну тильду. Подзапрос SELECT (ELT(5534=5534,1)) использует функцию ELT, которая возвращает N-й элемент из списка. В данном случае 5534=5534 — это истинное выражение, возвращающее 1. ELT(1,1) вернёт первый элемент, то есть 1. Таким образом, подзапрос вернёт число 1.
Итоговое XPath-выражение будет выглядеть как ~1~. Это некорректный XPath, поэтому EXTRACTVALUE вызовет ошибку вида: XPATH syntax error: '~1~'. В реальной атаке вместо 1 мог бы быть результат другого подзапроса, например, имя пользователя или хеш пароля. Атакующий может постепенно извлекать данные, подставляя разные SQL-выражения и анализируя текст ошибок.
Завершающая часть -- - — это комментарий. Два дефиса начинают комментарий в SQL, а пробел после них нужен для корректного завершения. Всё, что идёт после, игнорируется базой данных.
Зачем используется EXTRACTVALUE?
Метод с EXTRACTVALUE относится к так называемым error-based SQL-инъекциям. В отличие от UNION-инъекций, где данные выводятся непосредственно на странице, здесь информация извлекается через сообщения об ошибках. Это бывает полезно, когда приложение не отображает результаты запроса, но выводит ошибки базы данных.
До версии MySQL 5.7 функция EXTRACTVALUE была очень популярна среди пентестеров. Однако в более новых версиях (начиная с 5.7.9) она была удалена. Тем не менее, многие устаревшие системы всё ещё работают на старых версиях MySQL, поэтому угроза сохраняется.
Аналогичными функциями являются UPDATEXML и GTID_SUBSET, которые также могут вызывать ошибки с внедрёнными данными. Например, UPDATEXML(1,CONCAT(0x7e,(SELECT user()),0x7e),1) вызовет ошибку с именем текущего пользователя.
Пример работы инъекции
Предположим, на сервере выполняется запрос:
SELECT * FROM articles WHERE title LIKE '%"))) AND EXTRACTVALUE(5534,CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e))-- -%';
После подстановки пользовательского ввода запрос станет таким:
SELECT * FROM articles WHERE title LIKE '%%"))) AND EXTRACTVALUE(5534,CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e))-- -%';
MySQL попытается выполнить EXTRACTVALUE, что приведёт к ошибке. Если приложение выводит ошибки на экран, атакующий увидит сообщение, содержащее результат подзапроса. В нашем случае это будет ~1~, но если заменить подзапрос на (SELECT password FROM users LIMIT 1), то в ошибке отобразится пароль.
Как защититься от таких инъекций?
Основной метод защиты — использование параметризованных запросов (prepared statements). Они гарантируют, что пользовательский ввод никогда не будет интерпретирован как SQL-код. Вот несколько рекомендаций:
- Используйте подготовленные выражения во всех языках программирования: PHP (PDO), Python (psycopg2), Java (JDBC) и т.д.
- Экранируйте специальные символы, если параметризация невозможна. Но помните, что это менее надёжно.
- Ограничьте права пользователя базы данных: не давайте приложению прав на чтение системных таблиц, если это не требуется.
- Отключите вывод ошибок на продакшене. Ошибки должны логироваться, но не показываться пользователю.
- Регулярно обновляйте СУБД до актуальных версий, где уязвимые функции удалены или исправлены.
Также полезно использовать межсетевые экраны для веб-приложений (WAF), которые могут обнаруживать и блокировать попытки SQL-инъекций по сигнатурам, включая EXTRACTVALUE.
Заключение
Строка %"))) AND EXTRACTVALUE(5534,CONCAT(0x7e,((SELECT (ELT(5534=5534,1)))),0x7e))-- - — это яркий пример error-based SQL-инъекции, использующей функцию EXTRACTVALUE для извлечения данных через сообщения об ошибках. Понимание механики таких атак помогает разработчикам и специалистам по безопасности правильно защищать свои приложения. Главное правило — никогда не доверять пользовательскому вводу и всегда использовать параметризованные запросы.
Комментарии
—Войдите, чтобы оставить комментарий