Введение

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

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

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

Существует несколько типов SQL-инъекций: UNION-based, error-based, blind (boolean и time-based), stacked queries и другие. Рассматриваемая строка относится к классу error-based инъекций, где злоумышленник заставляет базу данных выдать сообщение об ошибке, содержащее полезные данные.

Разбор строки инъекции

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

  • %'))) — это начало, которое часто используется для закрытия предыдущих кавычек и скобок в исходном запросе. Символ % может быть частью URL-кодирования или обозначать любой символ в LIKE-запросе. Комбинация '))) закрывает строковый литерал и несколько круглых скобок, чтобы подготовить почву для внедрения.
  • AND EXTRACTVALUE(8089,CONCAT(0x7e,((SELECT (ELT(8089=8089,1)))),0x7e)) — это основная полезная нагрузка. Функция EXTRACTVALUE извлекает значение из XML-документа по заданному XPath-выражению. Если XPath-выражение некорректно, MySQL генерирует ошибку, которая включает результат вычисления выражения. Здесь злоумышленник передаёт в качестве первого аргумента число 8089 (произвольный XML-фрагмент), а в качестве второго — конкатенацию тильды (0x7e), результата подзапроса SELECT (ELT(8089=8089,1)) и ещё одной тильды.
  • -- - — это комментарий, который отсекает оставшуюся часть оригинального SQL-запроса, чтобы не возникло синтаксических ошибок.

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

Функция EXTRACTVALUE

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

Пример корректного использования:

SELECT EXTRACTVALUE('<a><b>текст</b></a>', '/a/b');
-- Вернёт 'текст'

Однако если XPath-выражение содержит синтаксическую ошибку, MySQL выдаёт ошибку вида XPATH syntax error: '...', где в кавычках может оказаться часть переданного выражения. Именно это и эксплуатируют злоумышленники: они конструируют XPath таким образом, чтобы в сообщении об ошибке отобразились нужные данные.

Функция ELT

ELT() — это функция MySQL, которая возвращает N-й элемент из списка строк. Синтаксис: ELT(N, строка1, строка2, ...). Например, ELT(2, 'a', 'b', 'c') вернёт 'b'.

В нашем случае используется ELT(8089=8089, 1). Выражение 8089=8089 всегда истинно и в числовом контексте равно 1. Таким образом, ELT(1, 1) вернёт 1. Это простейший пример, демонстрирующий работу функции. В реальных атаках вместо 1 может быть подставлен подзапрос, извлекающий данные из других таблиц, например, имена пользователей и пароли.

Функция CONCAT и шестнадцатеричные литералы

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

Подзапрос (SELECT (ELT(8089=8089,1))) возвращает 1, поэтому в ошибке появится строка ~1~. Если бы вместо 1 стоял, например, (SELECT version()), то в ошибке отобразилась бы версия MySQL.

Как работает атака целиком

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

После подстановки вредоносной строки запрос превращается в:

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

База данных пытается выполнить этот запрос. Часть '%'))) закрывает строку и скобки, затем следует AND EXTRACTVALUE(...). Функция EXTRACTVALUE получает некорректный XPath (поскольку второй аргумент — это строка, начинающаяся с тильды, что не является валидным XPath), и MySQL генерирует ошибку:

XPATH syntax error: '~1~'

Эта ошибка возвращается приложению, а затем отображается пользователю (если приложение не подавляет ошибки). Атакующий видит ~1~ и понимает, что инъекция работает. Далее он может заменить 1 на подзапрос, извлекающий нужные данные, например:
CONCAT(0x7e, (SELECT password FROM users LIMIT 1), 0x7e)

И в ошибке увидит пароль пользователя.

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

Основная цель — получить информацию из базы данных, когда другие методы (например, UNION-based) недоступны. Error-based инъекции особенно эффективны, когда приложение возвращает сообщения об ошибках базы данных. Они позволяют пошагово извлекать данные: имена таблиц, столбцов, логины, пароли, хеши и т.д.

Кроме того, такие инъекции могут использоваться для обхода аутентификации, выполнения административных операций или даже для компрометации всего сервера.

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

Существует несколько надёжных способов защиты:

  1. Использование подготовленных выражений (prepared statements) с параметризацией запросов. Это самый эффективный метод: данные пользователя никогда не встраиваются напрямую в SQL-код, а передаются как параметры.
  2. Экранирование специальных символов с помощью функций вроде mysqli_real_escape_string() (для MySQL). Однако этот метод уступает подготовленным выражениям и может быть обойдён при неверной настройке кодировки.
  3. Валидация и фильтрация входных данных — проверка типов, длины, допустимых символов. Например, если поле ожидает число, убедитесь, что оно действительно число.
  4. Ограничение прав пользователя базы данных: веб-приложение должно использовать отдельного пользователя СУБД с минимально необходимыми правами (только SELECT, INSERT, UPDATE, DELETE для нужных таблиц).
  5. Отключение вывода ошибок базы данных в продакшене. Сообщения об ошибках должны логироваться, но не показываться пользователю.
  6. Использование WAF (Web Application Firewall) — может помочь обнаружить и заблокировать попытки инъекций, но не является панацеей.

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

Заключение

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

Источники