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

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

Рассматриваемый запрос: %'))) AND EXTRACTVALUE(8167,CONCAT(0x7e,((SELECT (ELT(8167=8167,1)))),0x7e))-- - — это классический пример такой инъекции. Давайте разберём его по частям.

Анатомия запроса

Начнём с начала. Символы %'))) — это попытка закрыть предыдущие кавычки и скобки в оригинальном запросе, чтобы внедрить свой код. Далее идёт AND EXTRACTVALUE(8167, CONCAT(0x7e, ((SELECT (ELT(8167=8167,1)))), 0x7e)). Наконец, -- - — это комментарий, который отсекает оставшуюся часть оригинального запроса.

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

Роль CONCAT и 0x7e

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

Функция ELT и условие 8167=8167

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

Как это работает на практике

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

SELECT * FROM users WHERE id = '$id';

Если передать в параметр $id наш payload, запрос превратится в:

SELECT * FROM users WHERE id = '%'))) AND EXTRACTVALUE(8167,CONCAT(0x7e,((SELECT (ELT(8167=8167,1)))),0x7e))-- -';

MySQL выполнит EXTRACTVALUE, и поскольку второй аргумент не является корректным XPath, возникнет ошибка вида:

XPATH syntax error: '~1~'

Таким образом, атакующий видит результат выполнения вложенного запроса. Меняя содержимое ELT или добавляя подзапросы, можно извлекать произвольные данные: версию MySQL, текущего пользователя, имена таблиц и колонок, а затем и сами данные.

Какие данные можно извлечь?

  • Версия СУБД: SELECT version().
  • Текущий пользователь: SELECT user().
  • Имя базы данных: SELECT database().
  • Список таблиц: через information_schema.tables.
  • Данные из таблиц: например, логины и пароли.

Для этого вместо 1 в ELT подставляется соответствующий подзапрос. Например: ELT(1, (SELECT version())).

Меры защиты

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

  1. Использовать подготовленные выражения (prepared statements) с параметризацией. Это самый надёжный способ.
  2. Экранировать специальные символы при динамическом построении запросов.
  3. Ограничить права пользователя БД: не давать прав на чтение information_schema и лишних таблиц.
  4. Отключить вывод ошибок MySQL на продакшене, чтобы атакующий не видел сообщения.
  5. Использовать WAF (Web Application Firewall) для фильтрации подозрительных запросов.

Важно понимать, что EXTRACTVALUE — не единственная функция для error-based инъекций. Аналогично работают UPDATEXML, GTID_SUBSET и другие. Поэтому защита должна быть комплексной.

Заключение

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

Источники