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

SQL-инъекция — один из самых распространённых способов взлома веб-приложений. Злоумышленник внедряет вредоносный SQL-код в запрос, который выполняется базой данных. Если приложение не фильтрует входные данные, хакер может получить доступ к конфиденциальной информации, изменить или удалить данные.

Одной из разновидностей является error-based SQL-инъекция, при которой злоумышленник заставляет базу данных выдать сообщение об ошибке, содержащее полезные данные. Функция EXTRACTVALUE() в MySQL часто используется для таких атак, поскольку она возвращает текст из XML-документа по заданному XPath-выражению, а при ошибке выводит подробное сообщение.

В запросе пользователя %") OR EXTRACTVALUE(6913,CONCAT(0x7e,((SELECT (ELT(6913=6913,1)))),0x7e))-- - как раз демонстрируется попытка эксплуатации этой уязвимости. Давайте разберём его по частям.

Как работает функция EXTRACTVALUE?

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

Синтаксис: EXTRACTVALUE(xml_frag, xpath_expr).

Если XPath-выражение некорректно или не соответствует ни одному элементу, MySQL генерирует ошибку. В сообщении об ошибке может отображаться переданный фрагмент, что и используется для извлечения данных.

Разбор вредоносного запроса

Рассмотрим запрос: %") OR EXTRACTVALUE(6913,CONCAT(0x7e,((SELECT (ELT(6913=6913,1)))),0x7e))-- -

  • %") — закрывает предыдущее условие в SQL-запросе, например, в конструкции WHERE id = '%".
  • OR — логический оператор, добавляющий новое условие.
  • EXTRACTVALUE(6913, ...) — вызов уязвимой функции. Первый аргумент 6913 — произвольное число (может быть любым).
  • CONCAT(0x7e, ..., 0x7e) — конкатенация строк. 0x7e — шестнадцатеричное представление символа ~ (тильда), используется как разделитель, чтобы облегчить поиск извлечённых данных в сообщении об ошибке.
  • (SELECT (ELT(6913=6913,1))) — подзапрос, возвращающий результат функции ELT(). ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь 6913=6913 всегда истинно (1), поэтому ELT(1,1) вернёт 1 (первую строку). Это простейший пример; на практике вместо 1 подставляют подзапросы для извлечения имён таблиц, колонок или данных.
  • -- - — комментарий, обрезающий остаток оригинального SQL-запроса.

В результате MySQL попытается выполнить EXTRACTVALUE(6913, CONCAT('~', (SELECT 1), '~')). Поскольку первый аргумент не является корректным XML, функция выдаст ошибку, в тексте которой будет фигурировать строка ~1~. Таким образом, атакующий может видеть возвращаемые данные.

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

Используя эту технику, злоумышленник может получить:

  • версию MySQL (SELECT version());
  • имя текущей базы данных (SELECT database());
  • имя пользователя базы данных (SELECT user());
  • список таблиц и колонок из information_schema;
  • содержимое таблиц, включая логины, пароли, персональные данные.

Для извлечения данных по частям используются функции LIMIT, SUBSTRING, CONCAT и другие. Например, чтобы получить имя первой таблицы, можно подставить подзапрос: SELECT table_name FROM information_schema.tables LIMIT 0,1. При этом важно экранировать специальные символы и использовать шестнадцатеричное представление строк, чтобы избежать проблем с кавычками.

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

В отчётах по пентесту часто приводится похожий пример: ' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((SELECT CONCAT(0x7e, (SELECT user FROM data LIMIT 1,1), 0x7e)), FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) AND 1='1. Здесь используется GROUP BY с RAND() для вызова ошибки дублирования ключа, что также позволяет вывести данные. Но EXTRACTVALUE проще и не требует создания временных таблиц.

Важно понимать: подобные инъекции работают только при отсутствии должной фильтрации входных данных и при выводе ошибок базы данных на экран. В современных фреймворках (например, ORM) такие атаки обычно невозможны.

Как защититься от SQL-инъекций через ExtractValue?

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

Заключение

SQL-инъекция через EXTRACTVALUE — это мощный инструмент в руках злоумышленника, позволяющий извлекать данные из базы через сообщения об ошибках. Однако при правильной разработке приложений (параметризация, скрытие ошибок, ограничение прав) эта угроза полностью устраняется. Если вы обнаружили подобный запрос в логах, это признак попытки взлома — немедленно проверьте свой код на уязвимости.

Источники