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

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

В запросе пользователя фигурирует следующий payload:

'))) AND EXTRACTVALUE(4701,CONCAT(0x7e,((SELECT (ELT(4701=4701,1)))),0x7e))-- -

Это классическая инъекция, использующая ошибку синтаксического разбора XPath для вывода результата подзапроса в сообщении об ошибке. Ниже мы разберём каждый элемент этой конструкции.

Анатомия payload

Payload состоит из нескольких логических частей:

  • '))) — закрывающие кавычка и скобки, которые «ломают» исходный SQL-запрос, делая его синтаксически корректным для внедрения.
  • AND — логический оператор, позволяющий добавить условие к существующему запросу.
  • EXTRACTVALUE(4701, CONCAT(...)) — вызов уязвимой функции. Первый аргумент (4701) — произвольный XML-документ (число преобразуется в строку), второй — XPath-выражение, формируемое динамически.
  • CONCAT(0x7e, ((SELECT (ELT(4701=4701,1)))), 0x7e) — конкатенация тильды (0x7e — шестнадцатеричный код символа ~), результата подзапроса и ещё одной тильды.
  • -- - — комментарий, обрезающий остаток исходного запроса.

Зачем нужен символ 0x7e (тильда)?

Функция EXTRACTVALUE ожидает корректное XPath-выражение. Символ ~ не является допустимым в XPath, поэтому при попытке разбора строки, начинающейся с тильды, MySQL генерирует ошибку вида:

XPATH syntax error: '~1~'

В сообщении об ошибке отображается часть переданного XPath-выражения, а именно результат подзапроса, обёрнутый тильдами. Это позволяет злоумышленнику «прочитать» данные, которые возвращает вложенный SELECT. Тильда используется как маркер, чтобы отделить полезную нагрузку от служебных символов и облегчить автоматический парсинг результата.

Подзапрос с ELT и условием 4701=4701

Конструкция SELECT (ELT(4701=4701,1)) в данном случае возвращает строку '1'. Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Условие 4701=4701 всегда истинно, поэтому ELT возвращает первый элемент — 1. Это простейший пример; на практике вместо ELT с фиксированным значением злоумышленники подставляют подзапросы, извлекающие имена таблиц, столбцов или конкретные данные (например, логины и пароли).

В реальных атаках payload может выглядеть так:

EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), 0x7e))

Это позволяет получить список таблиц текущей базы данных в одном сообщении об ошибке.

Как работает инъекция по шагам

  1. Приложение формирует SQL-запрос, подставляя пользовательский ввод без должной очистки. Например: SELECT * FROM users WHERE id = 'ВВОД'.
  2. Злоумышленник передаёт строку '))) AND EXTRACTVALUE(...)-- -, которая закрывает кавычку и скобки, добавляет логическое условие и комментарий.
  3. Итоговый запрос выполняется, вызывая функцию EXTRACTVALUE с заведомо некорректным XPath.
  4. MySQL возвращает ошибку синтаксиса XPath, в тексте которой содержится результат подзапроса (например, ~1~).
  5. Злоумышленник (или автоматизированный инструмент) извлекает данные из сообщения об ошибке.

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

Меры защиты

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

  • Использовать параметризованные запросы (prepared statements) — это самый надёжный способ отделить код от данных.
  • Экранировать специальные символы при невозможности использования параметризации (например, с помощью функций экранирования драйвера БД).
  • Отключить вывод подробных ошибок в production-окружении, чтобы сообщения СУБД не попадали к пользователю.
  • Применять принцип минимальных привилегий для учётной записи базы данных, используемой веб-приложением.
  • Регулярно проводить аудит кода и использовать сканеры уязвимостей (SAST/DAST).

Функция EXTRACTVALUE сама по себе не является уязвимостью — проблема кроется в небезопасном построении SQL-запросов. Тем не менее, в современных версиях MySQL (начиная с 5.7) она помечена как устаревшая, и её использование не рекомендуется. Для извлечения данных из XML лучше применять функцию XMLQUERY или другие поддерживаемые методы.

Заключение

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

Источники