Что такое SQL-инъекция через ExtractValue?

SQL-инъекция — один из самых распространённых способов атаки на веб-приложения, при котором злоумышленник внедряет вредоносный SQL-код в запросы к базе данных. Существует несколько разновидностей таких атак, и одна из них основана на использовании функции ExtractValue в СУБД MySQL. Эта функция предназначена для извлечения значения из XML-документа по заданному XPath-выражению. Однако при неправильной обработке пользовательского ввода она может стать инструментом для извлечения данных из базы через механизм ошибок.

Запрос, который мы рассматриваем: %" AND EXTRACTVALUE(1383,CONCAT(0x7e,((SELECT (ELT(1383=1383,1)))),0x7e))-- - — является классическим примером error-based SQL-инъекции. Давайте разберём его по частям.

Анатомия запроса

Начнём с начала. Символ %" — это, вероятно, часть исходного запроса, куда внедряется инъекция. Например, в приложении может быть поиск по шаблону: SELECT * FROM users WHERE name LIKE '%".... Злоумышленник закрывает кавычку и добавляет логическое условие AND, за которым следует вызов функции EXTRACTVALUE.

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

В нашем случае первым аргументом идёт число 1383. Это не XML-документ, а просто число. MySQL автоматически преобразует его в строку '1383', что не является валидным XML. Но это не важно — функция всё равно попытается выполнить XPath-выражение, переданное вторым аргументом.

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

Итоговое XPath-выражение будет выглядеть как ~1~. Это синтаксически некорректный XPath, поэтому MySQL выдаст ошибку вида: XPATH syntax error: '~1~'. В тексте ошибки появятся данные, которые злоумышленник поместил между тильдами. В данном примере это просто '1', но в реальной атаке вместо ELT(1383=1383,1) может быть подзапрос, извлекающий, например, имя пользователя или хеш пароля.

Зачем нужны тильды и ELT?

Символ ~ используется как маркер, чтобы легко найти извлечённые данные в сообщении об ошибке. Функция ELT с условием 1383=1383 — это простейший способ вернуть константу. В более сложных атаках вместо неё применяются подзапросы к системным таблицам, например INFORMATION_SCHEMA.COLUMNS, для получения имён таблиц и колонок. Так, в одном из примеров из поисковой выдачи используется запрос:

+AND+ (SELECT+1+FROM+ (SELECT+COUNT (*),CONCAT ( (SELECT (SELECT+CONCAT (CAST (column_name+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+table_name=0x757365726e616d65+AND+table_schema=0x6c69746f666c6578+LIMIT 0,1),FLOOR (RAND (0)*2))x+FROM+INFORMATION ...

Здесь для извлечения имён колонок используется INFORMATION_SCHEMA.COLUMNS, а результат конкатенируется с тильдой. Подобные техники позволяют пошагово вытащить всю структуру базы данных и её содержимое.

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

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

Функция EXTRACTVALUE — не единственная, подверженная такой проблеме. Аналогично работают UPDATEXML и некоторые другие функции, работающие с XML. Они появились в MySQL 5.1 и стали популярны среди пентестеров благодаря простоте использования.

Важно понимать: сам по себе вызов EXTRACTVALUE с некорректным XPath не является уязвимостью. Уязвимость возникает, когда приложение позволяет пользователю влиять на SQL-запрос без должной очистки. В нашем примере инъекция внедряется через параметр, который, вероятно, подставляется в LIKE или WHERE.

Меры защиты

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

  • Использовать параметризованные запросы (prepared statements). Это самый надёжный способ, при котором данные пользователя передаются отдельно от SQL-кода и не могут изменить структуру запроса.
  • Экранировать специальные символы. Если по каким-то причинам параметризация невозможна, следует применять функции экранирования, например mysqli_real_escape_string в PHP.
  • Ограничить права пользователя БД. Учётная запись, от имени которой работает приложение, не должна иметь прав на чтение системных таблиц INFORMATION_SCHEMA без необходимости.
  • Отключить вывод ошибок СУБД. Настройте приложение так, чтобы детали ошибок не показывались пользователю, а записывались в лог.
  • Использовать WAF. Веб-application firewall может блокировать подозрительные запросы, содержащие сигнатуры SQL-инъекций.

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

Заключение

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

Источники