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

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

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

Как работает функция ExtractValue в MySQL

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

Например, запрос:

SELECT EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e));

вызовет ошибку вида: XPATH syntax error: '~5.7.23~'. Именно этот механизм и эксплуатируют хакеры: они помещают результат подзапроса внутрь XPath-выражения, а затем читают его из сообщения об ошибке, которое возвращается приложению.

Разбор конкретного инъекционного запроса

Рассмотрим строку: "))) AND EXTRACTVALUE(3972,CONCAT(0x7e,((SELECT (ELT(3972=3972,1)))),0x7e))-- -

  • "))) — это попытка закрыть ранее открытые скобки и кавычки в исходном SQL-запросе, чтобы внедрить свой код.
  • AND EXTRACTVALUE(...) — добавляет условие, которое всегда ложно или истинно, но главное — вызывает выполнение функции ExtractValue.
  • 3972 — произвольное число, первый аргумент функции. Может быть любым, так как важен второй аргумент.
  • CONCAT(0x7e, ..., 0x7e) — объединяет символ ~ (0x7e) с результатом подзапроса и ещё одним ~. Это делается для того, чтобы отделить выводимые данные в сообщении об ошибке.
  • (SELECT (ELT(3972=3972,1))) — подзапрос, который возвращает 1, если условие 3972=3972 истинно. ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь условие всегда истинно, поэтому возвращается 1. Это простейшая проверка, но на практике вместо этого подзапроса внедряют извлечение имён таблиц, колонок или хешей паролей.
  • -- - — комментарий, который обрезает оставшуюся часть оригинального запроса.

В результате выполнения такого запроса MySQL выдаст ошибку, в тексте которой будет содержаться значение, возвращённое подзапросом (в данном случае «1»), обрамлённое тильдами. Злоумышленник может заменить подзапрос на любой другой, например: (SELECT password FROM users LIMIT 1), и получить пароль в сообщении об ошибке.

Почему это опасно?

Error-based SQL-инъекции через ExtractValue позволяют атакующему:

  • Получить версию СУБД, имя базы данных, имена таблиц и колонок.
  • Извлечь логины, пароли, номера кредитных карт и другую конфиденциальную информацию.
  • В некоторых случаях — выполнить произвольные команды на сервере, если у базы данных есть соответствующие привилегии.

Особенность метода в том, что он не требует вывода данных напрямую на страницу — достаточно, чтобы сообщения об ошибках MySQL отображались пользователю или попадали в логи, к которым есть доступ.

Пример эксплуатации на практике

Предположим, на сайте есть страница с параметром id, который подставляется в запрос без экранирования: SELECT * FROM articles WHERE id = '$id'. Злоумышленник передаёт:

1' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e))-- -

Если приложение выводит ошибки MySQL, он увидит что-то вроде: XPATH syntax error: '~mydb~' и узнает имя текущей базы данных. Далее можно перебирать таблицы, колонки и извлекать данные.

Как защититься от SQL-инъекций через ExtractValue

Основной метод защиты — использование подготовленных выражений (prepared statements) с параметризацией запросов. Это исключает возможность внедрения SQL-кода, так как данные передаются отдельно от структуры запроса.

Дополнительные меры:

  • Отключите вывод ошибок MySQL в production-окружении. Сообщения об ошибках должны писаться в лог, а не отображаться пользователю.
  • Используйте ORM или библиотеки, которые автоматически экранируют входные данные.
  • Проводите валидацию всех входных параметров на стороне сервера.
  • Ограничьте привилегии пользователя базы данных, от имени которого работает приложение: запретите доступ к системным таблицам, если он не нужен.
  • Регулярно обновляйте MySQL и применяйте патчи безопасности.
  • Используйте WAF (Web Application Firewall) для фильтрации подозрительных запросов, но помните, что WAF — не панацея.

Заключение

ExtractValue — мощная функция MySQL, но при небрежной обработке пользовательского ввода она превращается в инструмент для кражи данных. Приведённый в запросе пример показывает, как простой подзапрос может быть использован для проверки уязвимости. Если вы разработчик, всегда используйте параметризованные запросы и не показывайте ошибки базы данных конечным пользователям. Если вы администратор — регулярно сканируйте приложения на наличие SQL-инъекций и следите за обновлениями безопасности.

Источники