Введение

Запрос %' AND EXTRACTVALUE(3619,CONCAT(0x7e,((SELECT (ELT(3619=3619,1)))),0x7e))-- - — это классический пример error-based SQL-инъекции в СУБД MySQL. Подобные конструкции используются злоумышленниками для извлечения данных из базы через сообщения об ошибках. В этой статье мы разберём, как работает каждый элемент payload, зачем нужны шестнадцатеричные префиксы и как защититься от таких атак.

Что такое error-based SQL-инъекция?

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

Синтаксис EXTRACTVALUE

EXTRACTVALUE(XML_фрагмент, XPath_выражение). Например, EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) вызовет ошибку вида XPATH syntax error: '~5.7.33~', где 0x7e — это тильда (~), служащая визуальным маркером для облегчения чтения.

Разбор payload по частям

Рассмотрим запрос детально:

  • %' — закрывает предыдущее строковое значение в уязвимом запросе и добавляет символ % для обхода фильтров (часто используется в LIKE-запросах).
  • AND — логический оператор, присоединяющий наше условие.
  • EXTRACTVALUE(3619, CONCAT(0x7e, ((SELECT (ELT(3619=3619,1)))), 0x7e)) — вызов уязвимой функции.
  • -- - — комментарий, обрезающий остаток оригинального SQL-запроса (пробел после двух дефисов обязателен в MySQL).

Первый аргумент: 3619

Число 3619 — произвольное целое. Оно не несёт смысловой нагрузки, но должно быть допустимым XML-фрагментом. Фактически это «заглушка», так как ошибка возникает на этапе вычисления XPath.

Второй аргумент: CONCAT(0x7e, ..., 0x7e)

Функция CONCAT склеивает строки. 0x7e — шестнадцатеричное представление символа ~ (тильда). Тильды обрамляют полезную нагрузку, чтобы в сообщении об ошибке было легко найти результат подзапроса. Без них данные сливались бы с текстом ошибки.

Подзапрос: SELECT (ELT(3619=3619,1))

ELT(N, строка1, строка2, ...) возвращает N-ю строку из списка. Выражение 3619=3619 всегда истинно (равно 1), поэтому ELT(1, 1) вернёт строку '1'. В данном случае это демонстрационный пример — в реальной атаке вместо 1 подставляется подзапрос, например (SELECT user()) или (SELECT password FROM users LIMIT 1).

Как это работает на практике

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

SELECT * FROM articles WHERE title LIKE '%ЗАПРОС%'

После подстановки payload запрос превращается в:

SELECT * FROM articles WHERE title LIKE '%%' AND EXTRACTVALUE(3619,CONCAT(0x7e,((SELECT (ELT(3619=3619,1)))),0x7e))-- -%'

MySQL выполняет EXTRACTVALUE, терпит неудачу и возвращает ошибку: XPATH syntax error: '~1~'. Злоумышленник видит результат выполнения подзапроса. Меняя содержимое ELT, можно извлекать имена таблиц, колонок, пароли и другую конфиденциальную информацию.

Почему это опасно?

Error-based инъекции позволяют получить данные напрямую, без слепого перебора. Это быстрее и надёжнее, чем boolean-based или time-based атаки. Особенно уязвимы приложения, которые выводят ошибки СУБД на экран. Даже если ошибки не показываются, злоумышленник может использовать другие каналы (логи, заголовки).

Методы защиты

Чтобы предотвратить подобные атаки, необходимо:

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

Заключение

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

Источники