Что такое EXTRACTVALUE и при чём тут SQL-инъекция?

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

Запрос AND EXTRACTVALUE(4597,CONCAT(0x7e,((SELECT (ELT(4597=4597,1)))),0x7e))-- - — это классический пример такой инъекции. Давайте разберём его по частям.

Разбор конструкции по элементам

1. Начало: AND

Оператор AND добавляется к легитимному SQL-запросу, чтобы внедрить дополнительное условие. Если исходный запрос был, например, SELECT * FROM users WHERE id = 1, то после подстановки получается SELECT * FROM users WHERE id = 1 AND EXTRACTVALUE(...). Это позволяет выполнить вредоносную функцию только при истинности предыдущего условия.

2. Функция EXTRACTVALUE(4597, ...)

Первый аргумент — 4597 — это произвольное число, которое не является корректным XML-документом. Второй аргумент — XPath-выражение, которое мы конструируем динамически. Когда MySQL пытается извлечь значение из не-XML строки, он генерирует ошибку. Именно на этом строится атака: в тексте ошибки можно вывести произвольные данные.

3. CONCAT(0x7e, ((SELECT (ELT(4597=4597,1)))), 0x7e)

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

Внутри CONCAT находится подзапрос (SELECT (ELT(4597=4597,1))). Разберём и его:

  • ELT(N, str1, str2, ...) — функция MySQL, возвращающая N-ю строку из списка. Если N=1, возвращается первая строка, если N=2 — вторая и т.д.
  • 4597=4597 — это логическое выражение, которое всегда истинно, поэтому в MySQL оно преобразуется в 1. Таким образом, ELT(1, 1) вернёт строку '1'.

В реальной атаке вместо 1 подставляются имена таблиц, колонок или другие данные, которые нужно извлечь. Например, ELT(1, (SELECT table_name FROM information_schema.tables LIMIT 1)).

4. Завершение: -- -

Двойной дефис с пробелом или без — это комментарий в SQL. Он «отрезает» остаток оригинального запроса, чтобы синтаксис остался корректным. Символ - после пробела добавлен для обхода некоторых фильтров, которые могут удалять -- без пробела.

Как это работает на практике

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

SELECT * FROM articles WHERE id = 1 AND EXTRACTVALUE(4597,CONCAT(0x7e,((SELECT (ELT(4597=4597,1)))),0x7e))-- -

MySQL выполнит функцию EXTRACTVALUE с первым аргументом 4597, который не является XML. Второй аргумент — строка вида ~1~. При попытке извлечь XPath из не-XML возникнет ошибка:

XPATH syntax error: '~1~'

В сообщении об ошибке мы видим ~1~. Если заменить ELT(4597=4597,1) на подзапрос, возвращающий, например, версию базы данных (SELECT version()), то в ошибке появится версия MySQL. Так пошагово злоумышленник может извлечь любые данные: имена таблиц, логины, пароли.

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

Error-based инъекции через EXTRACTVALUE (и аналогичную функцию UPDATEXML) позволяют обходить фильтры, которые блокируют UNION-запросы или слепые инъекции. Они дают быстрый отклик: не нужно перебирать символы по одному, как в blind SQLi — сервер сам возвращает данные в тексте ошибки. Это делает атаку эффективной и опасной.

Как защититься от подобных инъекций?

  1. Используйте подготовленные выражения (prepared statements). Это самый надёжный способ. Параметризованные запросы не позволяют пользовательскому вводу влиять на структуру SQL.
  2. Экранируйте специальные символы. Если по каким-то причинам нельзя использовать prepared statements, применяйте функции экранирования, например mysqli_real_escape_string.
  3. Ограничьте права пользователя БД. Учётная запись, от имени которой работает приложение, не должна иметь прав на чтение information_schema или системных таблиц.
  4. Включите логирование и мониторинг. Подозрительные запросы с EXTRACTVALUE, CONCAT, ELT должны вызывать тревогу.
  5. Обновляйте СУБД. В новых версиях MySQL некоторые функции могут быть отключены или работать иначе, но полагаться только на это нельзя.

Вывод

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

Источники