Что такое SQL-инъекция с EXTRACTVALUE?

SQL-инъекция — это один из самых распространённых способов атаки на веб-приложения, при котором злоумышленник внедряет вредоносный SQL-код в запросы к базе данных. Строка, которую вы видите: %")))) AND EXTRACTVALUE(5600,CONCAT(0x7e,((SELECT (ELT(5600=5600,1)))),0x7e))-- - — это не случайный набор символов, а тщательно сконструированная инъекция, использующая особенности функций MySQL для извлечения информации через сообщения об ошибках.

Давайте разберём её по частям.

Структура инъекции

Начнём с начала. Последовательность %")))) — это попытка закрыть существующие кавычки и скобки в оригинальном SQL-запросе, чтобы внедрить свой код. Символ % часто используется в URL-кодировании, но здесь он, вероятно, часть строки. Далее идёт AND EXTRACTVALUE(...) — это условие, которое добавляется к запросу. Если оно истинно, запрос выполняется дальше; если ложно — нет. Но главное — это вызов функции EXTRACTVALUE.

Функция EXTRACTVALUE

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

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

Функция ELT и условие 5600=5600

ELT(N, str1, str2, ...) возвращает N-ю строку из списка. В нашем случае ELT(5600=5600,1). Выражение 5600=5600 всегда истинно, то есть равно 1. Значит, ELT(1,1) вернёт строку '1'. Это простейший пример: инъекция проверяет, что база данных отвечает, и возвращает константу. В реальных атаках вместо 1 может быть подзапрос, извлекающий имя пользователя, хэш пароля и т.д.

Таким образом, CONCAT создаст строку ~1~. Затем EXTRACTVALUE(5600, '~1~') попытается интерпретировать 5600 как XML и применить XPath ~1~. Это вызовет ошибку, в тексте которой будет фигурировать ~1~ — то есть результат нашего подзапроса. Злоумышленник увидит его в ответе сервера.

Зачем нужен -- - в конце?

Двойной дефис -- в SQL — это комментарий до конца строки. Пробел после него и дефис - часто добавляют, чтобы гарантировать, что комментарий будет корректно распознан в разных СУБД. Это позволяет отсечь оставшуюся часть оригинального запроса, которая могла бы помешать выполнению инъекции.

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

Предположим, уязвимый запрос выглядит так: SELECT * FROM users WHERE id = '$input'. Если передать нашу строку, запрос станет:

SELECT * FROM users WHERE id = '%")))) AND EXTRACTVALUE(5600,CONCAT(0x7e,((SELECT (ELT(5600=5600,1)))),0x7e))-- -'

База данных попытается выполнить это, но из-за синтаксической ошибки в EXTRACTVALUE вернёт сообщение, содержащее ~1~. Если бы вместо 1 стоял подзапрос, например (SELECT password FROM users LIMIT 1), то в ошибке отобразился бы пароль.

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

Такие инъекции позволяют злоумышленнику извлекать произвольные данные из базы: логины, пароли, номера кредитных карт, персональную информацию. Они могут привести к полной компрометации системы. Особенно опасны слепые инъекции, когда данные извлекаются посимвольно через анализ поведения сервера, но в данном случае используется явная ошибка — это быстрее и проще.

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

Основной метод — использование параметризованных запросов (prepared statements). Они разделяют код и данные, поэтому внедрение SQL-кода становится невозможным. Например, в PHP с PDO:

$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $input]);

Дополнительные меры:

  • Экранирование специальных символов (но лучше параметризация).
  • Ограничение прав пользователя базы данных (не давать прав на чтение системных таблиц).
  • Валидация входных данных (например, если ожидается число, проверять, что это число).
  • Использование ORM, которые автоматически параметризуют запросы.
  • Регулярное обновление СУБД и фреймворков.

Также рекомендуется отключить вывод ошибок базы данных на продакшене, чтобы злоумышленник не мог получить текст ошибки. Вместо этого логируйте ошибки на сервере.

Заключение

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

Источники