Введение в error-based SQL-инъекции

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

Что такое EXTRACTVALUE?

EXTRACTVALUE — это встроенная функция MySQL (и MariaDB), предназначенная для извлечения значения из XML-документа по заданному XPath-выражению. Синтаксис: EXTRACTVALUE(XML_фрагмент, XPath_выражение). Если XPath-выражение синтаксически некорректно, MySQL генерирует ошибку, в тексте которой отображается переданное выражение. Именно это свойство и используется для error-based инъекций: злоумышленник может поместить в XPath результат подзапроса, и он попадёт в сообщение об ошибке.

Например, запрос SELECT EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) вызовет ошибку вида XPATH syntax error: '~5.7.33~', раскрывая версию СУБД.

Функция ELT и её роль

ELT (от англ. Element) — это функция MySQL, которая возвращает N-й элемент из списка строк. Синтаксис: ELT(N, строка1, строка2, ...). Если N=1, возвращается первая строка, и т.д. В контексте инъекции ELT часто используется для управления логикой: например, ELT(2729=2729,1) всегда вернёт 1, потому что условие истинно. Это позволяет создать безобидный на первый взгляд подзапрос, который на самом деле просто возвращает константу.

В нашем примере ELT(2729=2729,1) возвращает 1. Это число затем преобразуется в строку и подставляется в CONCAT вместе с символами ~ (0x7e).

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

Рассмотрим запрос по частям:

  • ')))) — закрывающие скобки и кавычки, необходимые для завершения оригинального SQL-запроса, в который внедряется инъекция. Количество скобок зависит от структуры исходного запроса.
  • AND EXTRACTVALUE(2729,CONCAT(0x7e,((SELECT (ELT(2729=2729,1)))),0x7e)) — основная часть инъекции. Здесь EXTRACTVALUE вызывается с первым аргументом 2729 (произвольное число, может быть любым), а второй аргумент — это XPath-выражение, которое заведомо некорректно, потому что начинается с символа ~ (тильда). Внутри CONCAT выполняется подзапрос SELECT ELT(2729=2729,1), возвращающий 1. Таким образом, XPath-выражение будет выглядеть как ~1~, что вызовет ошибку.
  • -- - — комментарий, обрезающий остаток оригинального запроса.

В результате MySQL выдаст ошибку: XPATH syntax error: '~1~'. Хотя в данном примере возвращается просто 1, заменив 1 на любой другой подзапрос (например, (SELECT database())), можно извлечь произвольные данные.

Зачем нужен CONCAT с 0x7e?

Символ 0x7e — это шестнадцатеричное представление тильды (~). Тильда используется как маркер, чтобы отделить выводимые данные от остального текста ошибки. Это облегчает автоматизированный парсинг результата. Кроме того, тильда не является допустимым символом в XPath, что гарантирует возникновение ошибки.

Пошаговый пример эксплуатации

Предположим, уязвимый запрос выглядит так: SELECT * FROM users WHERE id = '1'. Злоумышленник подставляет:

1')))) AND EXTRACTVALUE(2729,CONCAT(0x7e,((SELECT (ELT(2729=2729,1)))),0x7e))-- -

После подстановки запрос становится:

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

MySQL выполняет его, и при вычислении EXTRACTVALUE возникает ошибка, которая возвращается в ответе приложения. Если приложение отображает ошибки БД, злоумышленник увидит сообщение, содержащее ~1~. Заменив 1 на (SELECT version()), он получит версию СУБД, и так далее.

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

С помощью этой техники можно получить:

  • Версию MySQL/MariaDB
  • Имя текущей базы данных
  • Имена таблиц и столбцов
  • Логины и пароли пользователей
  • Любые другие данные, доступные в рамках текущих привилегий

Ограничение — длина выводимой строки (обычно около 32 символов), поэтому для извлечения длинных значений используют посимвольное извлечение с помощью SUBSTRING.

Меры защиты

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

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

Заключение

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

Источники