Введение

В логах веб-сервера или в коде приложения можно встретить странную конструкцию: %')))) AND EXTRACTVALUE(7421,CONCAT(0x7e,((SELECT (ELT(7421=7421,1)))),0x7e))-- -. Для неспециалиста это выглядит как набор случайных символов, но на самом деле это классический пример SQL-инъекции, основанной на ошибках (error-based SQL injection). Подобные payload'ы используются злоумышленниками для извлечения данных из базы данных через сообщения об ошибках, которые возвращает СУБД.

В этой статье мы разберём, что означает каждый элемент этой полезной нагрузки, как она работает в MySQL и, главное, как защитить своё веб-приложение от подобных атак.

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

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

СУБД MySQL, в отличие от некоторых других, часто возвращает подробные сообщения об ошибках, которые могут включать фрагменты данных. Это и используется в атаке.

Разбор полезной нагрузки

Рассмотрим строку %')))) AND EXTRACTVALUE(7421,CONCAT(0x7e,((SELECT (ELT(7421=7421,1)))),0x7e))-- - по частям.

1. Начальные символы: %'))))

Символ % — это URL-кодирование пробела (в некоторых контекстах). Далее идут закрывающие кавычки и скобки: ')))). Они нужны, чтобы «закрыть» оригинальный SQL-запрос, в который внедряется инъекция. Например, если уязвимый запрос выглядит как SELECT * FROM users WHERE id = ('$input'), то подстановка %')))) закроет кавычку, скобку и позволит добавить свои условия.

2. Оператор AND

Логический оператор AND используется для присоединения дополнительного условия к существующему WHERE. Это позволяет атакующему выполнить свой код только если исходный запрос возвращает истину (или наоборот).

3. Функция EXTRACTVALUE()

EXTRACTVALUE(XML_frag, XPath_expr) — это встроенная функция MySQL, которая извлекает значение из XML-фрагмента по заданному XPath-выражению. Однако она имеет особенность: если XPath-выражение содержит недопустимые символы, MySQL генерирует ошибку, в тексте которой отображается переданный XML-фрагмент. Это и есть ключевой момент для error-based инъекции.

В нашем случае первым аргументом идёт число 7421 (произвольное число, часто используемое как идентификатор), а вторым — результат CONCAT(0x7e, ..., 0x7e).

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

0x7e — это шестнадцатеричное представление символа тильда ~. Функция CONCAT объединяет строки. Здесь она оборачивает извлекаемые данные в тильды, чтобы их было легко найти в сообщении об ошибке. Например, если мы хотим извлечь имя пользователя, оно окажется между двумя ~.

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

ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Выражение 7421=7421 всегда истинно (1), поэтому ELT(1,1) вернёт 1. Это простейший случай. В реальной атаке вместо 1 может быть подзапрос, возвращающий имя таблицы, версию MySQL, имя пользователя и т.д. Например: ELT(1, (SELECT table_name FROM information_schema.tables LIMIT 1)).

Таким образом, вторая часть CONCAT — это результат подзапроса, который и будет выведен в ошибке.

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

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

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

Предположим, уязвимый запрос: SELECT * FROM products WHERE category = '$cat'. Атакующий передаёт в параметр cat строку %')))) AND EXTRACTVALUE(7421,CONCAT(0x7e,((SELECT (ELT(7421=7421,1)))),0x7e))-- -. После подстановки запрос станет таким:

SELECT * FROM products WHERE category = '')))) AND EXTRACTVALUE(7421,CONCAT(0x7e,((SELECT (ELT(7421=7421,1)))),0x7e))-- -'

MySQL выполнит EXTRACTVALUE, и поскольку XPath-выражение ~1~ содержит недопустимый символ (тильда не является допустимым началом XPath), возникнет ошибка: XPATH syntax error: '~1~'. В этом сообщении мы видим 1 — результат подзапроса. Если вместо 1 подставить, например, (SELECT version()), то в ошибке появится версия MySQL.

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

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

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

  • Используйте параметризованные запросы (prepared statements). Это самый надёжный способ: данные пользователя никогда не попадают в текст SQL-запроса напрямую.
  • Экранируйте специальные символы. Если по каким-то причинам параметризация невозможна, применяйте функции экранирования, например mysqli_real_escape_string() в PHP.
  • Ограничьте права пользователя БД. Не давайте веб-приложению прав на чтение information_schema без необходимости.
  • Отключайте вывод ошибок MySQL в продакшене. Сообщения об ошибках не должны попадать в ответы сервера.
  • Используйте WAF (Web Application Firewall). Современные WAF могут обнаруживать и блокировать подобные payload'ы.

Заключение

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

Источники