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

Строка %')))) AND EXTRACTVALUE(6400,CONCAT(0x7e,((SELECT (ELT(6400=6400,1)))),0x7e))-- - представляет собой классический пример error-based SQL-инъекции для СУБД MySQL. Она предназначена для внедрения в уязвимый SQL-запрос с целью заставить сервер выдать сообщение об ошибке, содержащее полезные данные. Этот метод относится к категории инъекций, основанных на ошибках (error-based), и часто используется злоумышленниками для извлечения информации из базы данных, когда прямой вывод данных невозможен.

В данной статье мы детально разберём, как работает этот запрос, какие функции MySQL задействованы, почему применяются шестнадцатеричные префиксы и как защититься от подобных атак.

Разбор структуры инъекции

Рассмотрим каждый элемент запроса по порядку.

1. Начальные символы: %'))))

Эти символы предназначены для «закрытия» предыдущих частей оригинального SQL-запроса. В зависимости от контекста уязвимости, злоумышленник подбирает комбинацию кавычек и скобок, чтобы корректно завершить незакрытые конструкции и внедрить свой код. Например, если исходный запрос выглядел как SELECT * FROM users WHERE name = '$input', то вставка %')))) может закрыть строку и несколько скобок, делая возможным добавление логического оператора AND.

2. Логический оператор AND

Оператор AND используется для присоединения дополнительного условия к исходному запросу. В контексте инъекции он позволяет внедрить вызов опасной функции, которая выполнится, если предыдущие условия истинны. Это ключевой элемент, превращающий безобидный запрос в атаку.

3. Функция EXTRACTVALUE(6400, CONCAT(...))

EXTRACTVALUE — это встроенная функция MySQL (до версии 8.0), предназначенная для извлечения значения из XML-документа по указанному XPath-выражению. Синтаксис: EXTRACTVALUE(XML_фрагмент, XPath_выражение). Если XPath-выражение синтаксически неверно, MySQL генерирует ошибку, которая включает в себя переданное выражение. Злоумышленники используют это для вывода данных: они подставляют в XPath результат подзапроса, и если он содержит недопустимые символы, сервер возвращает ошибку с этим значением.

В нашем случае первым аргументом идёт число 6400 — это произвольный XML-фрагмент (фактически не являющийся корректным XML, но функция всё равно пытается его обработать). Второй аргумент — CONCAT(0x7e, (SELECT (ELT(6400=6400,1))), 0x7e).

4. Функция CONCAT и шестнадцатеричный код 0x7e

CONCAT объединяет строки. Здесь она склеивает символ ~ (тильда), результат подзапроса и ещё одну тильду. 0x7e — это шестнадцатеричное представление ASCII-кода символа ~. Тильда используется как маркер, чтобы облегчить поиск выведенных данных в сообщении об ошибке: всё, что находится между двумя тильдами, является результатом внедрённого подзапроса.

5. Подзапрос (SELECT (ELT(6400=6400,1)))

Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. В данном случае ELT(6400=6400, 1) — условие 6400=6400 всегда истинно (возвращает 1), поэтому ELT(1, 1) вернёт строку '1'. Это простейший пример, демонстрирующий работу механизма. В реальной атаке вместо 1 мог бы быть подзапрос, извлекающий, например, имя пользователя или хеш пароля из таблицы users.

Таким образом, CONCAT формирует строку вида ~1~, которая передаётся в EXTRACTVALUE как XPath. Поскольку ~1~ не является допустимым XPath-выражением, MySQL выдаёт ошибку: XPATH syntax error: '~1~'. В этой ошибке злоумышленник видит результат выполнения подзапроса.

6. Комментарий -- -

Два дефиса и пробел (или дефис) обозначают начало комментария в SQL. Всё, что идёт после --, игнорируется сервером. Это позволяет отсечь остаток оригинального запроса, который мог бы помешать выполнению инъекции. Пробел после дефисов обязателен в некоторых реализациях, но в MySQL допустимо и -- -.

Зачем нужна такая инъекция?

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

Функция EXTRACTVALUE — не единственная; аналогично работают UPDATEXML, GTID_SUBSET и другие. Однако EXTRACTVALUE долгое время была популярна из-за простоты и надёжности.

Меры защиты

Чтобы предотвратить подобные атаки, необходимо:

  • Использовать подготовленные выражения (prepared statements) с параметризацией запросов. Это самый эффективный способ, так как данные не интерпретируются как SQL-код.
  • Экранировать специальные символы при невозможности использования подготовленных выражений. Но это менее надёжно.
  • Отключить вывод подробных ошибок СУБД на продакшене. Сообщения об ошибках должны логироваться, но не показываться пользователю.
  • Обновить MySQL до версии 8.0+, где функция EXTRACTVALUE удалена (начиная с 8.0.17 она официально не рекомендуется, а в 8.0.17+ может быть отключена). Однако это не решает проблему полностью, так как есть другие функции.
  • Применять WAF (Web Application Firewall) для фильтрации подозрительных запросов, хотя это не гарантирует защиту.

Пример уязвимого кода

Рассмотрим фрагмент на PHP, который может быть уязвим:

$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = '$id'";
$result = mysqli_query($conn, $query);

Если передать id=1' AND EXTRACTVALUE(6400,CONCAT(0x7e,(SELECT version()),0x7e))-- -, то в случае вывода ошибок злоумышленник увидит версию MySQL.

Заключение

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

Помните: даже одна неэкранированная кавычка может привести к полной компрометации базы данных. Всегда проверяйте входящие данные и никогда не доверяйте пользовательскому вводу.

Источники