Что это за строка и откуда она берётся

Встречая в логах веб-сервера или в поле ввода что-то вроде ")))) AND EXTRACTVALUE(3599,CONCAT(0x7e,((SELECT (ELT(3599=3599,1)))),0x7e))-- -, многие задаются вопросом: что это и какова её цель? Это классический пример error-based SQL-инъекции для СУБД MySQL. Такая строка предназначена для внедрения в уязвимый SQL-запрос с целью заставить базу данных вернуть данные через сообщение об ошибке.

Подобные конструкции часто появляются в автоматизированных сканерах уязвимостей, например sqlmap, или в ручных атаках. Они не являются вредоносным кодом сами по себе, но служат инструментом для проверки защищённости веб-приложения и потенциального извлечения конфиденциальной информации.

Как работает error-based SQL-инъекция

Основная идея error-based инъекции — заставить сервер базы данных выдать сообщение об ошибке, которое содержит результат выполнения внедрённого SQL-запроса. В MySQL для этого часто используют функции EXTRACTVALUE или UPDATEXML. Они принимают два аргумента: первый — XML-фрагмент, второй — XPath-выражение. Если XPath-выражение синтаксически некорректно, MySQL возвращает ошибку, в тексте которой отображается переданное значение.

В нашем примере:

EXTRACTVALUE(3599, CONCAT(0x7e, (SELECT (ELT(3599=3599,1))), 0x7e))

Первый аргумент — число 3599, которое не является корректным XML. Второй аргумент — конкатенация тильды (0x7e — шестнадцатеричное представление символа ~), результата подзапроса и ещё одной тильды. Подзапрос SELECT (ELT(3599=3599,1)) возвращает 1, так как условие 3599=3599 истинно. Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь N=1, поэтому возвращается строка '1'.

В итоге XPath-выражение становится чем-то вроде ~1~. Символ ~ не является допустимым началом XPath-выражения, поэтому MySQL генерирует ошибку: XPATH syntax error: '~1~'. В этом сообщении и «утекает» результат подзапроса.

Зачем нужны множественные скобки и комментарий

Строка начинается с ")))) — это закрывающие скобки и кавычки, которые помогают «закрыть» оригинальный SQL-запрос, если он был построен с использованием динамического конструирования. Злоумышленник подбирает количество скобок и кавычек, чтобы синтаксис стал корректным для внедрения.

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

Пример уязвимого запроса

Предположим, веб-приложение формирует запрос так:

$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = '" . $id . "'";

Если пользователь передаст 1' AND EXTRACTVALUE(3599,CONCAT(0x7e,(SELECT (ELT(3599=3599,1))),0x7e))-- -, то итоговый запрос станет:

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

MySQL выполнит условие id = '1', затем вычислит EXTRACTVALUE, что приведёт к ошибке, и вернёт её клиенту. Если приложение отображает ошибки БД, злоумышленник увидит результат.

Какие данные можно извлечь

Используя эту технику, атакующий может посимвольно извлекать любые данные из базы: имена таблиц, столбцов, логины и пароли пользователей, другую конфиденциальную информацию. Для этого в подзапрос вместо ELT(3599=3599,1) подставляются конструкции, возвращающие нужные значения, например:

  • SELECT version() — версия MySQL;
  • SELECT database() — имя текущей базы данных;
  • SELECT user() — текущий пользователь;
  • SELECT table_name FROM information_schema.tables LIMIT 1 — имена таблиц.

Ограничение длины вывода в сообщении об ошибке (обычно около 32 символов) заставляет применять функции SUBSTRING или MID для посимвольного извлечения.

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

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

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

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

  • Экранирование специальных символов с помощью mysqli_real_escape_string (менее надёжно, чем подготовленные выражения).
  • Валидация и фильтрация входных данных (например, приведение к целому числу, если ожидается ID).
  • Ограничение привилегий пользователя БД: не давайте веб-приложению прав на чтение information_schema и других системных таблиц без необходимости.
  • Отключение вывода ошибок БД в продакшене: сообщения об ошибках не должны попадать в ответ клиенту.
  • Использование WAF (Web Application Firewall) для блокировки подозрительных запросов.

Что делать, если вы нашли такую строку в логах

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

  1. Используются ли в коде параметризованные запросы.
  2. Отключён ли вывод ошибок БД для посетителей.
  3. Обновлены ли CMS и библиотеки до последних версий.
  4. Ведётся ли мониторинг подозрительной активности.

Если сайт написан на популярной CMS (WordPress, Joomla, Bitrix), убедитесь, что все плагины и модули обновлены, а также установлены средства защиты.

Заключение

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

Источники