Что такое SQL-инъекция через ExtractValue?

SQL-инъекция — один из самых распространённых способов взлома веб-приложений. Когда пользовательский ввод попадает в SQL-запрос без должной очистки, злоумышленник может внедрить собственные команды. Среди множества техник особое место занимает error-based SQL-инъекция — метод, при котором хакер заставляет базу данных выдать сообщение об ошибке, содержащее конфиденциальные данные. Функция EXTRACTVALUE в MySQL как раз позволяет реализовать такую атаку.

В этой статье мы детально разберём конкретный payload: %" OR EXTRACTVALUE(3023,CONCAT(0x7e,((SELECT (ELT(3023=3023,1)))),0x7e))-- -. Вы узнаете, как он устроен, зачем нужны отдельные его части и какие меры защиты помогут предотвратить подобные вторжения.

Как работает функция EXTRACTVALUE в MySQL

EXTRACTVALUE — встроенная функция MySQL, предназначенная для извлечения значения из XML-документа по заданному XPath-выражению. Синтаксис:

EXTRACTVALUE(XML_документ, XPath_выражение)

Если XPath-выражение синтаксически некорректно, MySQL генерирует ошибку, в текст которой попадает переданная строка. Это и используется в error-based инъекциях: злоумышленник подставляет вместо корректного XPath выражение, содержащее результат подзапроса, и сервер возвращает его в сообщении об ошибке.

Например, запрос SELECT EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) вызовет ошибку вида XPATH syntax error: '~5.7.33~', раскрыв версию СУБД.

Разбор payload: %" OR EXTRACTVALUE(3023,CONCAT(0x7e,((SELECT (ELT(3023=3023,1)))),0x7e))-- -

Рассмотрим каждую часть запроса по порядку.

  • %" — начало внедрения. Символ % и кавычка используются для «разрыва» исходного SQL-запроса, чтобы закрыть строковый литерал и добавить условие OR.
  • OR — логический оператор, который делает условие истинным при любом раскладе, если внедрённая часть вернёт ошибку (в error-based инъекциях это не важно, так как цель — вызвать ошибку, а не получить истинное условие).
  • EXTRACTVALUE(3023, ...) — вызов уязвимой функции. Первый аргумент (3023) — произвольное число, имитирующее XML-документ (фактически не используется).
  • CONCAT(0x7e, ((SELECT (ELT(3023=3023,1)))), 0x7e) — конкатенация тильды (0x7e), результата подзапроса и ещё одной тильды. Тильды служат маркерами, чтобы облегчить поиск вывода в сообщении об ошибке.
  • SELECT (ELT(3023=3023,1)) — подзапрос, возвращающий строку '1'. ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь 3023=3023 — всегда истина (1), поэтому ELT(1, 1) вернёт '1'. Это простейшая демонстрация; на практике вместо '1' подставляют имя пользователя, хэш пароля и т.п.
  • -- - — комментарий, который «отрезает» оставшуюся часть оригинального запроса, чтобы синтаксис стал корректным.

В результате MySQL пытается выполнить EXTRACTVALUE(3023, CONCAT('~', '1', '~')), что даёт XPath-выражение ~1~. Это синтаксически неверно, и СУБД возвращает ошибку: XPATH syntax error: '~1~'. Злоумышленник видит этот ответ и понимает, что инъекция работает.

Практическое применение и модификации

В реальных атаках вместо ELT(3023=3023,1) внедряют подзапросы, извлекающие данные из системных таблиц. Например, чтобы получить имя текущей базы данных, используют:

OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e))-- -

Для получения списка таблиц:

OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()), 0x7e))-- -

Аналогично можно извлечь имена столбцов и содержимое таблиц users. Ограничение — длина сообщения об ошибке (обычно около 32 символов), поэтому часто прибегают к посимвольному выводу через substring или используют другие функции, например updatexml.

Чем опасна такая инъекция?

Error-based SQL-инъекции позволяют злоумышленнику полностью прочитать содержимое базы данных, включая логины, пароли, персональные данные клиентов. В некоторых случаях через них можно выполнить произвольный код на сервере (если СУБД имеет соответствующие привилегии). Последствия — утечка конфиденциальной информации, финансовые потери, репутационный ущерб.

Как защититься от SQL-инъекций

Основная причина уязвимости — недостаточная фильтрация пользовательского ввода. Вот ключевые меры защиты:

  1. Параметризованные запросы (prepared statements). Используйте их везде, где возможно. Они разделяют код и данные, делая инъекции невозможными.
  2. Экранирование спецсимволов. Если параметризация недоступна, применяйте функции экранирования (например, mysqli_real_escape_string в PHP).
  3. Валидация входных данных. Проверяйте типы, форматы и диапазоны значений. Не доверяйте данным от клиента.
  4. Принцип наименьших привилегий. Учётная запись, от имени которой работает приложение, должна иметь только необходимые права. Не используйте root для подключения из веб-скриптов.
  5. Регулярное обновление СУБД и фреймворков. Исправляйте известные уязвимости.
  6. Использование WAF. Веб-application firewall может блокировать подозрительные запросы, но не заменяет правильного кодирования.

Также рекомендуется проводить аудит кода и пентесты для выявления подобных брешей до того, как их обнаружат злоумышленники.

Заключение

Payload %" OR EXTRACTVALUE(3023,CONCAT(0x7e,((SELECT (ELT(3023=3023,1)))),0x7e))-- - — это классический учебный пример error-based SQL-инъекции через функцию EXTRACTVALUE в MySQL. Он демонстрирует, как злоумышленник может заставить базу данных выдать данные в сообщении об ошибке. Понимание механики таких атак необходимо для построения надёжной защиты. Помните: лучшая защита — это параметризованные запросы и внимательное отношение к любому пользовательскому вводу.