Что такое EXTRACTVALUE и при чём тут SQL-инъекция?
Функция EXTRACTVALUE — это встроенная функция MySQL (и некоторых других СУБД), предназначенная для извлечения значения из XML-документа по заданному XPath-выражению. Синтаксис: EXTRACTVALUE(xml_fragment, xpath_expr). Она возвращает текст узла, соответствующего XPath. Однако при неправильной обработке пользовательского ввода эта функция может стать инструментом для проведения error-based SQL-инъекций — атак, при которых злоумышленник извлекает данные из базы, провоцируя вывод сообщений об ошибках.
Запрос, который мы разбираем, выглядит так: )) AND EXTRACTVALUE(5465,CONCAT(0x7e,((SELECT (ELT(5465=5465,1)))),0x7e))-- -. Это классический пример эксплуатации уязвимости через функцию EXTRACTVALUE. Давайте разберём его по частям.
Анатомия вредоносного запроса
1. Контекст внедрения
Начинается всё с двух закрывающих скобок )). Они нужны, чтобы «закрыть» ранее открытые скобки в оригинальном SQL-запросе, который формируется на сервере. Например, если приложение выполняет запрос вида SELECT * FROM users WHERE id = (SELECT ...), то инъекция с )) позволяет выйти за пределы исходной конструкции и добавить своё условие.
2. Оператор AND
Далее следует AND — логический оператор, который позволяет присоединить дополнительное условие. Если основное условие истинно, то результат выполнения всего выражения будет зависеть от правой части. Это нужно, чтобы внедряемая функция выполнилась.
3. Вызов EXTRACTVALUE
Центральный элемент — EXTRACTVALUE(5465, CONCAT(...)). Первый аргумент — произвольное число (здесь 5465), оно не играет роли. Второй аргумент — это XPath-выражение, которое формируется динамически с помощью CONCAT. Если XPath-выражение синтаксически некорректно, MySQL выдаст ошибку, в тексте которой будет содержаться переданное значение. Именно на этом строится атака: злоумышленник может подставить в XPath результат подзапроса, и этот результат отобразится в сообщении об ошибке.
4. CONCAT и ELT
Внутри CONCAT используется 0x7e — это шестнадцатеричное представление символа ~ (тильда). Тильда добавляется для того, чтобы отделить выводимые данные от остального текста ошибки. Далее идёт подзапрос (SELECT (ELT(5465=5465,1))). Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь условие 5465=5465 всегда истинно (возвращает 1), поэтому ELT вернёт первый элемент — строку '1'. В реальной атаке вместо '1' мог бы быть, например, database(), user() или другой подзапрос, извлекающий конфиденциальные данные.
5. Комментарий
Завершается всё последовательностью -- -. Двойной дефис с пробелом — это комментарий в SQL, который «отрезает» оставшуюся часть оригинального запроса, чтобы синтаксис оставался корректным.
Как это работает на практике
Предположим, уязвимый запрос выглядит так: SELECT * FROM articles WHERE id = '$id'. Злоумышленник передаёт в параметр id строку 1')) AND EXTRACTVALUE(5465,CONCAT(0x7e,(SELECT database()),0x7e))-- -. После подстановки получается:
SELECT * FROM articles WHERE id = '1')) AND EXTRACTVALUE(5465,CONCAT(0x7e,(SELECT database()),0x7e))-- -'СУБД попытается вычислить XPath-выражение, которое будет содержать имя текущей базы данных, например ~mydb~. Так как это невалидный XPath, возникнет ошибка: XPATH syntax error: '~mydb~'. В результате атакующий увидит имя базы данных прямо в ответе сервера.
Аналогично можно извлечь версию MySQL, имена таблиц, столбцов, логины и пароли пользователей — всё, что позволяет синтаксис SQL. Для этого достаточно заменить database() на соответствующий подзапрос.
Почему EXTRACTVALUE стала популярной?
До версии MySQL 5.7 функция EXTRACTVALUE была широко распространена, и многие веб-приложения не фильтровали входные данные должным образом. Ошибки, содержащие результаты подзапросов, отображались прямо на странице, что давало злоумышленнику мгновенную обратную связь. Даже если вывод ошибок отключён, атакующий может использовать слепые методы, но error-based инъекции считаются наиболее простыми и быстрыми.
С выходом MySQL 8.0 функция EXTRACTVALUE была объявлена устаревшей (deprecated), а в некоторых сборках удалена. Однако на практике до сих пор встречаются серверы с более старыми версиями, поэтому угроза сохраняется.
Меры защиты
Чтобы предотвратить эксплуатацию подобных уязвимостей, необходимо:
- Использовать подготовленные выражения (prepared statements) с параметризацией запросов. Это самый надёжный способ, так как данные не попадают в текст SQL-запроса.
- Применять ORM (например, Hibernate, Entity Framework), которые автоматически экранируют опасные символы.
- Валидировать и фильтровать входные данные на стороне сервера, особенно если они используются в динамически формируемых SQL-конструкциях.
- Отключать вывод подробных ошибок в production-окружении. Сообщения об ошибках должны логироваться, а пользователю показываться общая страница.
- Регулярно обновлять СУБД и применять патчи безопасности.
- Использовать WAF (Web Application Firewall) для обнаружения и блокировки попыток SQL-инъекций.
Также рекомендуется проводить регулярные аудиты кода и пентесты, чтобы выявлять уязвимости до того, как ими воспользуются злоумышленники.
Заключение
Запрос )) AND EXTRACTVALUE(5465,CONCAT(0x7e,((SELECT (ELT(5465=5465,1)))),0x7e))-- - — это типичный пример error-based SQL-инъекции, использующей функцию EXTRACTVALUE для извлечения данных через сообщения об ошибках. Понимание механики таких атак помогает разработчикам и специалистам по безопасности правильно выстраивать защиту. Помните: безопасность — это процесс, а не разовое действие. Используйте параметризованные запросы, обновляйте компоненты и следите за тем, чтобы ошибки не раскрывали лишнюю информацию.
Комментарии
—Войдите, чтобы оставить комментарий