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

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

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

Детальный разбор запроса

Начнём с начала: ' — одиночная кавычка, которая закрывает строковый литерал в уязвимом SQL-запросе. Далее следует AND EXTRACTVALUE(...). Функция EXTRACTVALUE принимает два аргумента: первый — строка XML, второй — XPath-выражение. В нашем случае первый аргумент — число 8067, которое автоматически преобразуется в строку '8067'. Это невалидный XML, но MySQL всё равно попытается выполнить XPath-запрос.

Второй аргумент — CONCAT(0x7e,((SELECT (ELT(8067=8067,1)))),0x7e). Функция CONCAT объединяет строки. 0x7e — это шестнадцатеричное представление символа тильды (~). Он используется как маркер, чтобы облегчить поиск результата в сообщении об ошибке. Внутри CONCAT находится подзапрос SELECT (ELT(8067=8067,1)).

ELT — это функция MySQL, которая возвращает N-й элемент из списка строк. Синтаксис: ELT(N, строка1, строка2, ...). В нашем случае N — это результат выражения 8067=8067, которое всегда истинно (равно 1). Значит, ELT(1, 1) вернёт первый элемент — число 1. То есть подзапрос вернёт строку '1'.

Таким образом, второй аргумент EXTRACTVALUE станет строкой ~1~. Функция EXTRACTVALUE попытается применить XPath-выражение ~1~ к строке '8067', что вызовет ошибку, потому что это невалидный XPath. В сообщении об ошибке MySQL отобразит переданное XPath-выражение, то есть ~1~. Это и есть цель атаки — заставить базу данных вывести на экран (или в лог) произвольные данные.

Завершающая часть -- - — это комментарий. Два дефиса начинают комментарий в SQL, а пробел после них (иногда заменяемый на другой символ) нужен для корректного завершения. Всё, что идёт после, игнорируется.

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

В данном примере подзапрос возвращает просто число 1, что демонстрирует работоспособность метода. В реальной атаке вместо ELT(8067=8067,1) подставляются другие SQL-выражения, например, (SELECT user()) или (SELECT password FROM users LIMIT 1). Тогда в сообщении об ошибке появится имя пользователя базы данных или пароль. Используя эту технику, атакующий может посимвольно извлекать любые данные из базы: имена таблиц, колонок, логины, хеши паролей и т.д.

Error-based SQL-инъекции опасны тем, что даже при отсутствии вывода данных на странице, сообщения об ошибках могут содержать конфиденциальную информацию. Часто разработчики не отключают вывод ошибок в production-окружении, что делает этот тип атак очень эффективным.

Как защититься от таких атак?

Основной способ защиты — использование подготовленных выражений (prepared statements) с параметризацией запросов. Это исключает возможность внедрения SQL-кода, так как пользовательский ввод передаётся отдельно от самого запроса и не интерпретируется как SQL. Дополнительные меры:

  • Отключить вывод подробных сообщений об ошибках на production-серверах. Ошибки должны логироваться, но не показываться пользователю.
  • Использовать ORM (например, Doctrine, Hibernate) или библиотеки для работы с БД, которые автоматически экранируют опасные символы.
  • Проводить регулярные аудиты кода и сканирования уязвимостей (SAST, DAST).
  • Ограничить права пользователя базы данных, от имени которого работает приложение: только необходимые привилегии.
  • Применять Web Application Firewall (WAF) для фильтрации подозрительных запросов.

Важно понимать, что простая фильтрация кавычек не всегда помогает: злоумышленники могут использовать различные кодировки, обходные символы и т.д. Поэтому параметризация — самый надёжный метод.

Примеры других error-based инъекций

Помимо EXTRACTVALUE, в MySQL существуют и другие функции, вызывающие ошибки с полезной нагрузкой: UPDATEXML (аналогична EXTRACTVALUE, но для обновления XML), GTID_SUBSET, EXP (переполнение), RAND с GROUP BY. В СУБД PostgreSQL, MSSQL, Oracle также есть свои техники. Например, в MSSQL можно использовать convert(int, (select ...)) для приведения типов и получения ошибки.

Конкретно наш запрос с EXTRACTVALUE и ELT часто встречается в отчётах пентестеров и сканерах уязвимостей (например, sqlmap). Он позволяет быстро проверить, уязвимо ли приложение к error-based SQL-инъекции, и если да — извлечь данные. Стоит отметить, что начиная с MySQL 5.7 функция EXTRACTVALUE была удалена? Нет, она всё ещё присутствует, но помечена как deprecated в некоторых версиях. Однако на практике она работает во многих распространённых версиях MySQL и MariaDB.

Заключение

Разобранный запрос — это компактный и эффективный эксплойт для error-based SQL-инъекции. Он использует функцию EXTRACTVALUE для генерации ошибки, содержащей результат подзапроса, обёрнутый в тильды для удобства. Понимание таких конструкций необходимо как специалистам по безопасности для тестирования, так и разработчикам для осознания важности защиты. Никогда не доверяйте пользовательскому вводу и всегда используйте параметризованные запросы — это золотое правило веб-разработки.

Источники