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

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

В этой статье мы детально разберём, как работает данная инъекция, какие функции MySQL задействованы и как защитить свой сайт от подобных атак.

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

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

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

Функция EXTRACTVALUE

Функция EXTRACTVALUE(XMLType_instance, XPath_expression) принимает XML-документ и XPath-выражение, а возвращает скалярное значение выбранного узла. Если XPath-выражение синтаксически неверно, MySQL выдаёт ошибку, в которой цитируется часть выражения. Это и используется для внедрения.

В нашем случае в качестве первого аргумента передаётся число 3355, которое автоматически преобразуется в XML? Нет, MySQL ожидает XMLType, но при передаче числа возникает ошибка другого рода. Однако в контексте инъекции это не важно — главное, что функция вызывается и генерирует ошибку с нашим XPath.

Функция CONCAT

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

Функция ELT

ELT(N, str1, str2, ...) возвращает N-ю строку из списка. В нашем случае ELT(3355=3355,1) — условие 3355=3355 всегда истинно, что в числовом контексте даёт 1. Значит, ELT(1,1) вернёт строку '1'. Это простейший пример; в реальных атаках вместо 1 подставляют подзапрос, например (SELECT version()) или (SELECT password FROM users LIMIT 1).

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

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

Пошаговый разбор инъекции

Рассмотрим, как эта строка внедряется в уязвимый запрос. Предположим, исходный запрос выглядит так:

SELECT * FROM articles WHERE id = '$id';

Если параметр $id не фильтруется, злоумышленник может подставить нашу строку. Тогда запрос примет вид:

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

Обратите внимание: в начале строки стоит %") — это закрывает кавычку и скобку, чтобы синтаксис остался корректным. Далее идёт AND EXTRACTVALUE(...), который выполнится, если предыдущее условие истинно (или просто как часть выражения). В результате MySQL попытается вычислить EXTRACTVALUE и выдаст ошибку, например:

XPATH syntax error: '~1~'

В этом сообщении между тильдами как раз и находится результат подзапроса. В нашем примере это 1, но если вместо 1 подставить (SELECT database()), то в ошибке появится имя текущей базы данных.

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

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

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

Поскольку ошибка выводится на страницу (или попадает в логи), атакующий может посимвольно извлекать информацию, используя функции вроде SUBSTRING и LIMIT.

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

Основной способ защиты — использование параметризованных запросов (prepared statements). Это надёжно отделяет данные от SQL-кода. Дополнительные меры:

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

Важно понимать, что EXTRACTVALUE — не единственная функция для error-based инъекций. UPDATEXML, GTID_SUBSET, JSON_KEYS и другие также могут использоваться. Поэтому защита должна быть комплексной.

Пример безопасного кода на PHP

Вместо уязвимого кода:

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

Следует использовать PDO с подготовленными выражениями:

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

В этом случае даже если злоумышленник передаст нашу строку, она будет воспринята как обычное значение, а не как SQL-код.

Заключение

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

Источники