Введение

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

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

SQL-инъекция (SQL injection) — это один из самых распространённых способов взлома веб-приложений. Злоумышленник вставляет вредоносный SQL-код в поля ввода или параметры URL, чтобы заставить базу данных выполнить нежелательные действия: получить доступ к конфиденциальным данным, изменить или удалить информацию.

Существует несколько разновидностей SQL-инъекций:

  • Классическая инъекция — когда результаты запроса отображаются на странице.
  • Слепая инъекция — когда данные извлекаются по косвенным признакам (например, по времени ответа или по сообщениям об ошибках).
  • Инъекция на основе ошибок (error-based) — когда информация извлекается через сообщения об ошибках базы данных. Именно к этому типу относится наш пример с EXTRACTVALUE.

Функция EXTRACTVALUE в MySQL

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

EXTRACTVALUE(XML_фрагмент, XPath_выражение)

Например, EXTRACTVALUE('<a><b>текст</b></a>', '/a/b') вернёт текст.

Однако если XPath-выражение некорректно, MySQL выдаёт ошибку, в тексте которой может содержаться переданное значение. Этим и пользуются злоумышленники: они подставляют в XPath результат подзапроса, и если он не является валидным XPath, база данных возвращает ошибку с этим значением. Так происходит утечка данных.

Функция CONCAT

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

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

Рассмотрим строку )) AND EXTRACTVALUE(5900,CONCAT(0x7e,((SELECT (ELT(5900=5900,1)))),0x7e))-- - детально.

  • )) — закрывающие скобки, которые завершают предыдущее выражение в оригинальном запросе. Злоумышленник подставляет свой код после них.
  • AND — логический оператор, который добавляет условие к исходному запросу. Если исходный запрос был истинным, добавление AND ... заставит базу данных вычислить и наше выражение.
  • EXTRACTVALUE(5900, ...) — вызов функции. Первый аргумент 5900 — это произвольное число, которое будет использовано как XML-фрагмент. Так как это не XML, функция всё равно попытается разобрать его и выдаст ошибку, но перед этим вычислит второй аргумент.
  • CONCAT(0x7e, ((SELECT (ELT(5900=5900,1)))), 0x7e) — второй аргумент. Здесь 0x7e — это шестнадцатеричное представление символа ~. Внутри CONCAT выполняется подзапрос SELECT (ELT(5900=5900,1)).
  • ELT(5900=5900,1) — функция ELT возвращает N-й элемент из списка. Если условие 5900=5900 истинно (а это всегда так), то выражение принимает значение 1, и ELT возвращает первый элемент — число 1. Фактически это заглушка, которая всегда возвращает 1. В реальной атаке вместо ELT(5900=5900,1) мог бы быть, например, (SELECT database()) или (SELECT version()).
  • -- - — комментарий, который отсекает оставшуюся часть оригинального SQL-запроса, чтобы не возникло синтаксической ошибки.

Таким образом, при выполнении этого запроса MySQL попытается вычислить EXTRACTVALUE(5900, CONCAT('~', '1', '~')). Поскольку 5900 не является корректным XML, функция выдаст ошибку вида: XPATH syntax error: '~1~'. В сообщении об ошибке появится значение ~1~. Если вместо 1 подставить подзапрос, возвращающий имя базы данных, то в ошибке отобразится это имя. Так злоумышленник может извлекать любые данные.

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

Основная цель — получить информацию о структуре базы данных и её содержимом. С помощью error-based инъекций можно узнать:

  • имя текущей базы данных (database());
  • версию MySQL (version());
  • список таблиц и колонок из information_schema;
  • логины и пароли пользователей, хранящиеся в таблицах.

Функция EXTRACTVALUE позволяет вытаскивать данные по одному значению за раз, что делает процесс медленным, но эффективным при отсутствии других каналов.

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

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

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

  • Использовать ORM (например, Hibernate, Entity Framework) или библиотеки, которые автоматически экранируют данные.
  • Проводить валидацию и фильтрацию всех входных данных на стороне сервера.
  • Ограничить права пользователя базы данных, от имени которого работает приложение: запретить доступ к information_schema, если он не нужен.
  • Отключить вывод ошибок базы данных в production-окружении, чтобы злоумышленник не мог использовать error-based инъекции.
  • Регулярно обновлять СУБД и фреймворки, так как в новых версиях MySQL функции EXTRACTVALUE и UPDATEXML были удалены (начиная с MySQL 8.0.17) именно из-за их опасности.

Заключение

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

Источники