Введение

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

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

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

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

Разбор запроса `))) AND EXTRACTVALUE(4343,CONCAT(0x7e,((SELECT (ELT(4343=4343,1)))),0x7e))-- -

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

  • `))) — закрывающие символы, которые, вероятно, завершают оригинальный SQL-запрос и его условия. Символ обратного апострофа (`) используется для экранирования идентификаторов в MySQL. Три закрывающие скобки могут закрывать вложенные конструкции исходного запроса.
  • AND — логический оператор, добавляющий условие к запросу.
  • EXTRACTVALUE(4343, CONCAT(...)) — вызов функции EXTRACTVALUE с двумя аргументами: первый — произвольное число (4343), второй — результат конкатенации. Функция ожидает, что второй аргумент будет корректным XPath-выражением. Если это не так, генерируется ошибка с текстом переданного значения.
  • CONCAT(0x7e, ((SELECT (ELT(4343=4343,1)))), 0x7e) — конкатенация символа тильды (0x7e — шестнадцатеричный код символа ~), результата подзапроса и ещё одной тильды. Тильды служат маркерами, чтобы в сообщении об ошибке легко было выделить извлечённые данные.
  • SELECT (ELT(4343=4343,1)) — подзапрос, возвращающий результат функции ELT(). ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь 4343=4343 — это логическое выражение, которое всегда истинно (1). Таким образом, ELT(1,1) вернёт строку '1'. Это простейший пример; в реальной атаке вместо этого подзапроса может быть запрос к системным таблицам, например, SELECT database() или SELECT table_name FROM information_schema.tables.
  • -- - — комментарий, который игнорирует оставшуюся часть оригинального запроса. В MySQL комментарий начинается с -- (два дефиса и пробел) или #. Здесь используется -- -, где второй дефис отделён пробелом, чтобы гарантировать корректное распознавание комментария.

Таким образом, данный payload пытается внедрить в оригинальный запрос вызов EXTRACTVALUE, который должен вызвать ошибку с текстом, содержащим результат подзапроса. В приведённом примере результат — просто '1', но в реальной атаке здесь были бы конфиденциальные данные.

Как работает EXTRACTVALUE для извлечения данных?

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

XPATH syntax error: '~результат~'

Именно этот текст ошибки и видит атакующий. Подставляя в XPath-выражение результаты подзапросов, можно посимвольно или целиком извлекать имена баз данных, таблиц, колонок и даже хеши паролей.

Современные версии MySQL (начиная с 5.7.9) и MariaDB содержат исправления, но во многих устаревших системах эта уязвимость всё ещё эксплуатируется.

Примеры реальных атак

Вот как может выглядеть запрос для извлечения имени текущей базы данных:

`))) AND EXTRACTVALUE(4343,CONCAT(0x7e,(SELECT database()),0x7e))-- -

А для получения версии MySQL:

`))) AND EXTRACTVALUE(4343,CONCAT(0x7e,(SELECT version()),0x7e))-- -

Для извлечения имён таблиц из системной схемы information_schema:

`))) AND EXTRACTVALUE(4343,CONCAT(0x7e,(SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),0x7e))-- -

И так далее, с помощью LIMIT можно перебирать строки одну за другой.

Меры защиты

Чтобы предотвратить подобные атаки, необходимо:

  1. Использовать подготовленные выражения (prepared statements) с параметризацией запросов. Это самый надёжный способ, так как SQL-код и данные передаются раздельно.
  2. Применять ORM (например, Hibernate, Entity Framework), которые автоматически экранируют опасные символы.
  3. Проводить валидацию и фильтрацию входных данных. Особенно это касается полей, которые передаются в SQL-запросы.
  4. Ограничить права пользователя базы данных, от имени которого работает приложение. Не давайте ему доступ к системным таблицам без необходимости.
  5. Регулярно обновлять СУБД и веб-приложения. В новых версиях многие уязвимости закрыты.
  6. Использовать WAF (Web Application Firewall) для фильтрации подозрительных запросов.

Также рекомендуется проводить аудит кода и пентесты для выявления уязвимостей.

Заключение

Запрос `))) AND EXTRACTVALUE(4343,CONCAT(0x7e,((SELECT (ELT(4343=4343,1)))),0x7e))-- - — это классический пример error-based SQL-инъекции в MySQL. Понимание механики таких атак помогает разработчикам и администраторам лучше защищать свои системы. Помните, что безопасность — это процесс, а не одноразовое действие.

Источники