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

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

`)) AND EXTRACTVALUE(6027,CONCAT(0x7e,((SELECT (ELT(6027=6027,1)))),0x7e))-- -

Вы узнаете, как он работает, какие данные извлекает и почему это опасно.

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

Функция EXTRACTVALUE(XML_fragment, XPath_expression) извлекает значение из фрагмента XML, используя выражение XPath. Если XML-фрагмент некорректен или XPath-выражение содержит ошибку, MySQL возвращает сообщение об ошибке. Например:

SELECT EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e));

Здесь 0x7e — это шестнадцатеричное представление символа тильды (~). При выполнении такого запроса MySQL выдаст ошибку вида:

XPATH syntax error: '~5.7.33~'

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

Разбор конкретного запроса

Рассмотрим запрос по частям:

  • `)) — закрывающие скобки и кавычки, которые завершают оригинальный SQL-запрос приложения, делая возможным внедрение.
  • AND EXTRACTVALUE(6027, CONCAT(0x7e, ((SELECT (ELT(6027=6027,1)))), 0x7e)) — основная полезная нагрузка.
  • -- - — комментарий, который игнорирует оставшуюся часть оригинального запроса.

Внутри EXTRACTVALUE первым аргументом идёт число 6027 (произвольное), а вторым — результат CONCAT, который объединяет тильду, результат подзапроса и снова тильду. Подзапрос SELECT (ELT(6027=6027,1)) использует функцию ELT.

Функция ELT и её роль

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

Например, чтобы получить имя текущей базы данных, можно использовать:

AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e))

Сообщение об ошибке покажет имя базы.

Пошаговое выполнение атаки

Предположим, что на сайте есть уязвимый параметр id, и оригинальный запрос выглядит так:

SELECT * FROM articles WHERE id = '$id'

Злоумышленник передаёт:

1')) AND EXTRACTVALUE(6027,CONCAT(0x7e,((SELECT (ELT(6027=6027,1)))),0x7e))-- -

После подстановки запрос становится:

SELECT * FROM articles WHERE id = '1')) AND EXTRACTVALUE(6027,CONCAT(0x7e,((SELECT (ELT(6027=6027,1)))),0x7e))-- -'

MySQL выполняет EXTRACTVALUE, что вызывает ошибку с текстом XPATH syntax error: '~1~'. На первый взгляд безобидно, но заменив '1' на подзапрос, атакующий извлекает любые данные.

Извлечение данных из information_schema

Чтобы получить имена таблиц, используют запросы к information_schema.tables. Пример:

AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1), 0x7e))

Ошибка покажет первую таблицу. Меняя LIMIT 0,1 на 1,1 и так далее, перебирают все таблицы. Аналогично добывают колонки и данные.

Почему это опасно?

Error-based SQL-инъекция через ExtractValue позволяет атакующему:

  • Получить полную структуру базы данных (таблицы, колонки).
  • Извлечь конфиденциальные данные: логины, пароли, персональную информацию.
  • В некоторых случаях выполнить произвольные SQL-команды, если привилегии позволяют.

Особенность метода в том, что он работает даже при отсутствии вывода данных на странице — достаточно, чтобы сообщения об ошибках отображались или логировались.

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

Основной способ защиты — использование параметризованных запросов (prepared statements). Вместо подстановки переменных в SQL-строку, передавайте их как параметры. Например, на PHP с PDO:

$stmt = $pdo->prepare('SELECT * FROM articles WHERE id = :id');
$stmt->execute(['id' => $id]);

Это полностью исключает возможность инъекции, так как код и данные разделены.

Дополнительные меры:

  1. Отключите вывод ошибок СУБД на продакшене — это затруднит error-based атаки.
  2. Используйте ORM или query builder, которые автоматически экранируют данные.
  3. Проводите регулярные сканирования уязвимостей (например, с помощью sqlmap).
  4. Ограничьте права пользователя базы данных: не давайте ему доступ к information_schema без необходимости.
  5. Внедрите WAF (Web Application Firewall) для фильтрации подозрительных запросов.

Заключение

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

Источники