Что такое EXTRACTVALUE и при чём тут SQL-инъекция

Функция EXTRACTVALUE — это встроенная функция MySQL, предназначенная для работы с XML. Она принимает два строковых аргумента: фрагмент XML-разметки и выражение XPath (локатор). Функция возвращает текст (CDATA) первого текстового узла, который является дочерним для элемента или элементов, соответствующих XPath-выражению.

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

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

Разбор payload по элементам

Payload выглядит так:

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

Разберём каждый компонент:

  • %' — символ процента и одинарная кавычка. Обычно используется для завершения строкового литерала в уязвимом запросе. Символ % может быть частью LIKE-шаблона, а кавычка закрывает строку, позволяя внедрить произвольный SQL-код.
  • AND — логический оператор, который добавляет условие к исходному запросу. Если исходный запрос был истинным, добавление AND ... не изменит результат, но выполнит внедрённую функцию.
  • EXTRACTVALUE(3444, CONCAT(...)) — вызов уязвимой функции. Первый аргумент 3444 — произвольное число (фрагмент XML), второй аргумент — XPath-выражение, которое формируется динамически через CONCAT.
  • CONCAT(0x7e, ..., 0x7e) — конкатенация строк. 0x7e — это шестнадцатеричное представление символа ~ (тильда). Тильда используется как маркер, чтобы облегчить поиск выводимых данных в сообщении об ошибке.
  • (SELECT (ELT(3444=3444,1))) — подзапрос, который возвращает результат функции ELT. ELT(N, str1, str2, ...) возвращает N-ю строку из списка. В данном случае ELT(3444=3444, 1) — условие 3444=3444 всегда истинно (возвращает 1), поэтому ELT(1, 1) вернёт строку '1'. Это простейший пример; на практике вместо 1 может быть подзапрос, извлекающий данные (например, имя пользователя, хеш пароля).
  • -- - — комментарий. Два дефиса с пробелом (или -- -) закомментируют остаток исходного запроса, чтобы не нарушать синтаксис.

Как это работает: пошагово

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

SELECT * FROM users WHERE name = '$input';

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

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

MySQL выполнит:

  1. Условие name = '%' — вероятно, ложно, но это не важно, так как далее идёт AND.
  2. Функция EXTRACTVALUE попытается интерпретировать второй аргумент как XPath. Строка, переданная в XPath, будет выглядеть как ~1~ (тильда, результат ELT, тильда).
  3. Выражение ~1~ не является допустимым XPath, поэтому MySQL сгенерирует ошибку: XPATH syntax error: '~1~'.
  4. В тексте ошибки появится значение ~1~, которое можно увидеть в ответе сервера (если ошибки выводятся).

Таким образом, атакующий может заменить 1 в ELT на любой подзапрос, например (SELECT password FROM users LIMIT 1), и получить его результат в сообщении об ошибке.

Зачем нужны 0x7e и ELT

0x7e (тильда) служит разделителем. Поскольку XPath-ошибка выводит строку целиком, тильды помогают легко найти начало и конец извлечённых данных в длинном сообщении об ошибке. Кроме того, тильда не является специальным символом в XPath и не вызовет дополнительных проблем.

ELT — это функция, которая возвращает N-й элемент из списка. В контексте инъекции она используется для преобразования результата подзапроса в строку, которую можно передать в XPath. Например, ELT(1, (SELECT user())) вернёт имя текущего пользователя MySQL. Если результат подзапроса не строка, ELT автоматически приведёт его к строковому типу.

В простейшем случае ELT(3444=3444,1) — это демонстрация работоспособности инъекции: условие всегда истинно, поэтому возвращается 1. Это позволяет проверить, уязвим ли параметр, не извлекая реальные данные.

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

С помощью этой техники атакующий может получить практически любую информацию из базы данных, к которой у него есть доступ через SQL-запросы. Например:

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

Для каждого из этих значений потребуется отдельный запрос, так как error-based инъекция обычно возвращает только один результат за раз (если не использовать групповую конкатенацию).

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

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

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

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

Заключение

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

Источники