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

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

Рассматриваемый запрос — классический пример такой инъекции:

" AND EXTRACTVALUE(7095,CONCAT(0x7e,((SELECT (ELT(7095=7095,1)))),0x7e))-- -

Давайте разберём его по частям, чтобы понять логику атаки.

Анатомия инъекции: CONCAT, ELT и тильда

Основная цель — заставить сервер вывести в сообщении об ошибке результат выполнения вложенного запроса. Для этого используется цепочка функций:

  • EXTRACTVALUE(7095, ...) — первый аргумент (7095) здесь не важен, это просто произвольное число. Второй аргумент — XPath-выражение, которое формируется динамически.
  • CONCAT(0x7e, ..., 0x7e) — объединяет символ тильды (~, шестнадцатеричный код 0x7e) с результатом подзапроса. Тильда используется как маркер, чтобы легко найти выводимые данные в тексте ошибки.
  • SELECT (ELT(7095=7095,1)) — это вложенный запрос, который возвращает число 1. Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Если N=1, возвращается первый элемент — в данном случае 1. Условие 7095=7095 всегда истинно, поэтому ELT всегда вернёт 1.

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

Зачем это нужно злоумышленнику?

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

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

Сервер вернёт ошибку с названием базы данных. Аналогично извлекаются версии СУБД, имена пользователей и другие сведения. Поскольку ошибки часто отображаются на странице (особенно в режиме отладки), атакующий может пошагово «вытягивать» данные, используя LIMIT и смещения.

Почему EXTRACTVALUE, а не другие функции?

MySQL предоставляет несколько функций, вызывающих ошибки с пользовательскими данными. Помимо EXTRACTVALUE, часто используют UPDATEXML с аналогичным механизмом. EXTRACTVALUE удобен тем, что принимает два аргумента и не требует наличия XML-данных — достаточно некорректного XPath. Однако начиная с MySQL 5.7.9 функция была помечена как устаревшая, а в MySQL 8.0 удалена. Тем не менее, множество устаревших систем всё ещё работают на старых версиях, поэтому угроза сохраняется.

Пример из реальной практики

Предположим, на сайте есть страница с параметром id, который подставляется в SQL-запрос без экранирования. Злоумышленник отправляет:

https://example.com/product.php?id=1 AND EXTRACTVALUE(7095,CONCAT(0x7e,((SELECT (ELT(7095=7095,1)))),0x7e))-- -

Если приложение уязвимо, в ответе появится ошибка MySQL с текстом XPATH syntax error: '~1~'. Это сигнал, что инъекция возможна. Далее атакующий заменяет ELT(7095=7095,1) на подзапрос, извлекающий нужные данные, и получает их через сообщения об ошибках.

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

Основная причина успеха таких атак — отсутствие фильтрации входных данных и вывод ошибок СУБД на страницу. Вот ключевые меры защиты:

  1. Используйте подготовленные выражения (prepared statements). Это самый надёжный способ: параметры запроса передаются отдельно от SQL-кода, и внедрить произвольные конструкции не получится.
  2. Экранируйте специальные символы. Если prepared statements недоступны, применяйте функции экранирования (например, mysqli_real_escape_string).
  3. Отключите вывод ошибок на продакшене. Сообщения об ошибках должны логироваться, а не отображаться пользователю. Это лишает атакующего обратной связи.
  4. Обновляйте СУБД. В MySQL 8.0 функции EXTRACTVALUE и UPDATEXML удалены, что снижает риск.
  5. Используйте WAF. Веб-приложения-экраны могут блокировать подозрительные запросы, содержащие EXTRACTVALUE, CONCAT с hex-кодами и т.п.
  6. Проводите аудит кода. Регулярно сканируйте приложение на уязвимости с помощью инструментов (sqlmap, OWASP ZAP).

Заключение

Конструкция " AND EXTRACTVALUE(7095,CONCAT(0x7e,((SELECT (ELT(7095=7095,1)))),0x7e))-- - — это не просто случайный набор символов, а тщательно продуманный эксплойт для извлечения данных через ошибки MySQL. Понимание его работы помогает разработчикам осознать важность безопасного программирования и своевременного обновления компонентов. Если вы нашли подобный код в логах своего сайта, немедленно проверьте приложение на уязвимости и устраните их.

Источники