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

SQL-инъекция (SQLi) — один из самых опасных классов веб-уязвимостей, который уже много лет не покидает список OWASP Top 10. Атака позволяет злоумышленнику вмешиваться в выполнение SQL-запросов, читать, изменять или удалять данные. Среди множества техник особое место занимает error-based SQL-инъекция, при которой данные извлекаются через сообщения об ошибках базы данных. Одним из классических инструментов для такой атаки в MySQL является функция EXTRACTVALUE.

В этом материале мы детально разберём конкретный payload: ")) OR EXTRACTVALUE(5781,CONCAT(0x7e,((SELECT (ELT(5781=5781,1)))),0x7e))-- - — объясним, из каких частей он состоит, зачем нужны функции CONCAT и ELT, и как защитить приложение от подобных атак.

Функция EXTRACTVALUE в MySQL

EXTRACTVALUE — это встроенная функция MySQL, предназначенная для работы с XML. Она извлекает значение из фрагмента XML-документа по заданному XPath-выражению. Синтаксис прост:

EXTRACTVALUE(XML_frag, XPath_expr)

Функция возвращает текстовое содержимое узла, соответствующего XPath. Если XPath-выражение синтаксически некорректно, MySQL генерирует ошибку XPATH syntax error и включает в сообщение фрагмент переданного выражения. Именно эта особенность и делает EXTRACTVALUE идеальным инструментом для error-based инъекций: атакующий может «подсунуть» в XPath-аргумент результат подзапроса, и сервер вернёт его в тексте ошибки.

Важно: начиная с MySQL 5.7 функция EXTRACTVALUE помечена как устаревшая (deprecated), а в MySQL 8.0 удалена. Однако множество legacy-систем до сих пор работают на старых версиях, поэтому угроза сохраняется.

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

Рассмотрим строку ")) OR EXTRACTVALUE(5781,CONCAT(0x7e,((SELECT (ELT(5781=5781,1)))),0x7e))-- - поэлементно.

1. Закрывающие скобки и кавычки: "))

Символы ")) нужны, чтобы «закрыть» оригинальный SQL-запрос приложения. Разработчик, скорее всего, использовал конструкцию вида WHERE id = ("$input"). Атакующий подставляет свои символы, чтобы синтаксис остался корректным, и добавляет логический оператор OR.

2. Оператор OR

OR присоединяет вредоносное условие к существующему WHERE. Если исходное условие ложно, OR с внедрённым выражением может вернуть истину — или, как в нашем случае, вызвать выполнение опасной функции.

3. EXTRACTVALUE(5781, CONCAT(...))

Первый аргумент — произвольное число 5781 (может быть любым). Второй аргумент — результат CONCAT. Поскольку XPath-выражение формируется динамически и содержит запрещённые символы, MySQL сгенерирует ошибку, в тексте которой окажется результат вложенного подзапроса.

4. CONCAT(0x7e, ..., 0x7e)

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

5. (SELECT (ELT(5781=5781,1)))

ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Выражение 5781=5781 всегда истинно и в числовом контексте даёт 1. Значит, ELT(1, 1) вернёт первый элемент — строку "1". В реальной атаке вместо константы 1 подставляется подзапрос, извлекающий данные (например, имя базы, версию СУБД или хеши паролей). Здесь приведён «тестовый» вариант для проверки уязвимости: если сервер вернёт ошибку с тильдами и единицей внутри, точка инъекции подтверждена.

6. Комментарий -- -

Двойной дефис с пробелом (или -- -) — это комментарий в SQL. Он «отрезает» остаток оригинального запроса, чтобы лишние кавычки и скобки не сломали синтаксис. Пробел после -- обязателен в MySQL.

Как выглядит успешная атака

Если приложение уязвимо, в ответе сервера появится сообщение об ошибке вида:

XPATH syntax error: '~1~'

Атакующий видит: инъекция работает, данные выводятся. Далее вместо 1 он подставляет, например, SELECT version() или SELECT table_name FROM information_schema.tables LIMIT 1 — и посимвольно (или сразу) извлекает конфиденциальную информацию.

Чем опасна error-based инъекция

  • Утечка данных: через ошибки можно вытащить имена таблиц, столбцов, логины, пароли, номера карт.
  • Обход аутентификации: манипулируя условиями WHERE, можно войти под администратором без пароля.
  • Полный контроль над БД: в некоторых конфигурациях возможны чтение/запись файлов и выполнение команд ОС.
  • Скрытность: error-based атаки часто не оставляют явных следов в логах приложений, если ошибки не логируются должным образом.

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

Главное правило — никогда не конкатенировать пользовательский ввод в SQL-запросы. Рассмотрим надёжные меры:

  1. Параметризованные запросы (prepared statements). Это золотой стандарт. Данные передаются отдельно от SQL-кода, и СУБД не интерпретирует их как команды.
  2. Хранимые процедуры. При правильной реализации также снижают риск, но не заменяют параметризацию полностью.
  3. Валидация и whitelist. Если параметр должен быть числом — приводите его к числу. Если строкой из фиксированного набора — проверяйте по списку.
  4. Экранирование. Используйте встроенные функции экранирования драйвера БД, но помните: это менее надёжно, чем prepared statements.
  5. Минимальные привилегии. Учётная запись приложения не должна иметь прав на DROP, FILE, доступ к information_schema без необходимости.
  6. Отключение вывода ошибок. Пользователь не должен видеть текст SQL-ошибок. Логируйте их на сервере, а клиенту отдавайте обобщённое сообщение.
  7. WAF и мониторинг. Веб-application firewall может блокировать типовые payload'ы, но не полагайтесь только на него.
  8. Регулярные обновления. Переходите на актуальные версии MySQL, где EXTRACTVALUE удалена, а другие потенциально опасные функции ограничены.

Проверка собственного сайта

Чтобы убедиться, что ваше приложение не уязвимо, можно использовать легальные сканеры (например, sqlmap в тестовом окружении) или провести аудит кода. Ищите места, где SQL-запросы формируются через конкатенацию строк с участием переменных из $_GET, $_POST, cookies или HTTP-заголовков. Если такие места есть — срочно переписывайте их на prepared statements.

Итог

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

Источники