Что это за строка?

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

Подобные конструкции часто встречаются в отчётах сканеров уязвимостей и в логах веб-серверов, когда злоумышленник или автоматизированный инструмент (например, sqlmap) проверяет параметры на возможность инъекции. Внешне это выглядит как бессмысленный набор символов, но за ним стоит чёткий алгоритм извлечения данных.

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

Чтобы понять, как работает этот payload, разберём его на составные элементы:

  • %" — начало строки. Символ % часто используется в URL-кодировании, а " — это экранированная кавычка. Вместе они помогают «разорвать» исходный SQL-запрос, в который внедряется инъекция. Например, если оригинальный запрос был SELECT * FROM users WHERE name = '...', то вставка %" может закрыть строковый литерал и добавить условие AND.
  • AND — логический оператор, присоединяющий наше вредоносное выражение к оригинальному условию WHERE.
  • EXTRACTVALUE(5258, CONCAT(...)) — это ключевая функция MySQL, которая извлекает значение из XML-фрагмента. Она принимает два аргумента: XML-документ и XPath-выражение. Если XPath-выражение некорректно, MySQL выбрасывает ошибку, в тексте которой отображается переданный XML-фрагмент. Именно это и используется для вывода данных.
  • CONCAT(0x7e, ((SELECT (ELT(5258=5258,1)))), 0x7e) — конкатенация (склеивание) нескольких значений. 0x7e — это шестнадцатеричный код символа ~ (тильда). Он служит маркером, по которому потом легко найти выведенные данные в сообщении об ошибке. Внутри CONCAT находится подзапрос SELECT (ELT(5258=5258,1)).
  • ELT(5258=5258,1) — функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь 5258=5258 — это всегда истинное условие (1), поэтому ELT(1,1) вернёт строку '1'. В реальных атаках вместо 1 может быть подставлено имя таблицы, колонки или другая полезная информация. Например, ELT(1, database()) вернёт имя текущей базы данных.
  • -- - — комментарий в SQL. Всё, что идёт после --, игнорируется парсером. Пробел после -- необходим для корректного распознавания комментария в MySQL. Символ - добавлен для надёжности, чтобы избежать проблем с некоторыми парсерами.

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

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

SELECT * FROM articles WHERE id = '$_GET[id]'

Злоумышленник передаёт в параметр id нашу строку. После подстановки запрос превращается в:

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

MySQL пытается выполнить EXTRACTVALUE. Поскольку второй аргумент — это строка ~1~, которая не является корректным XPath-выражением, функция генерирует ошибку. В тексте ошибки будет что-то вроде:

XPATH syntax error: '~1~'

Таким образом, значение, возвращённое подзапросом, отображается в сообщении об ошибке. Если вместо ELT(5258=5258,1) подставить, например, ELT(1, version()), то в ошибке появится версия MySQL. Это и есть основа error-based SQL-инъекции.

Зачем это нужно злоумышленнику?

Error-based инъекции — один из самых удобных способов извлечения данных, потому что они не требуют слепого перебора (blind SQL injection) и работают даже тогда, когда результаты запроса не отображаются на странице. Достаточно, чтобы сервер выводил сообщения об ошибках базы данных. С помощью таких конструкций можно получить:

  • Имена таблиц и колонок
  • Логины и пароли пользователей
  • Хеши паролей
  • Другую конфиденциальную информацию

Стоит отметить, что начиная с MySQL 5.7 функция EXTRACTVALUE была объявлена устаревшей, а в MySQL 8.0 — удалена. Однако многие веб-приложения до сих пор работают на старых версиях, поэтому угроза сохраняется. В современных версиях для аналогичных атак используются другие функции, например updatexml или GTID_SUBSET.

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

Лучший способ предотвратить SQL-инъекции — использовать параметризованные запросы (prepared statements). Они гарантируют, что пользовательские данные никогда не будут интерпретированы как SQL-код. Дополнительные меры:

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

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

Заключение

Рассмотренная строка — это не случайный набор символов, а тщательно сконструированный эксплойт для MySQL. Понимание принципов его работы помогает разработчикам и специалистам по безопасности лучше защищать свои приложения. Никогда не доверяйте пользовательскому вводу и всегда используйте параметризованные запросы.

Источники