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

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

В запросе пользователя ))) AND EXTRACTVALUE(5365,CONCAT(0x7e,((SELECT (ELT(5365=5365,1)))),0x7e))-- - как раз демонстрируется такой приём. Давайте разберём его по частям.

Разбор вредоносного запроса

Строка начинается с трёх закрывающих скобок ))) — это попытка закрыть ранее открытые скобки в оригинальном SQL-запросе, чтобы внедрить свой код. Затем идёт AND EXTRACTVALUE(5365, CONCAT(...)). Функция ExtractValue принимает два аргумента: XML-документ и XPath-выражение. В качестве первого аргумента используется число 5365 — оно не является корректным XML, но это не важно, так как ошибка возникнет на этапе вычисления второго аргумента.

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

Когда MySQL пытается вычислить ExtractValue с XPath-выражением ~1~, он выдаёт ошибку: XPATH syntax error: '~1~'. Эта ошибка отображается на странице (если включён вывод ошибок), и злоумышленник видит результат выполнения подзапроса. Меняя подзапрос, можно извлекать произвольные данные из базы.

Важно: подобные запросы часто используются автоматизированными инструментами, например sqlmap, для эксплуатации уязвимостей.

Почему это работает?

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

В примере с ELT(5365=5365,1) возвращается 1, но на месте этого выражения может быть, например, (SELECT password FROM users LIMIT 1), и тогда ошибка покажет пароль. Однако стоит отметить, что в современных версиях MySQL (начиная с 5.7) вывод ошибок часто подавляется, и метод может не сработать без дополнительных условий.

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

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

  • Экранировать все пользовательские данные, попадающие в SQL-запросы.
  • Использовать ORM (например, Doctrine, Hibernate) или query builder.
  • Ограничить права пользователя базы данных, чтобы даже при успешной инъекции злоумышленник не мог выполнить опасные операции.
  • Отключить вывод подробных ошибок MySQL на продакшене.
  • Регулярно обновлять СУБД и фреймворки.

Также рекомендуется проводить аудит кода и использовать сканеры уязвимостей, например SQLMap для тестирования своих приложений (с разрешения владельца).

Пример уязвимого кода

Рассмотрим простой PHP-скрипт, уязвимый для данной инъекции:

$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = $id";
$result = mysqli_query($conn, $query);

Если передать id=1))) AND EXTRACTVALUE(5365,CONCAT(0x7e,((SELECT (ELT(5365=5365,1)))),0x7e))-- -, то запрос станет таким:

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

Это приведёт к синтаксической ошибке или выполнению внедрённого кода в зависимости от контекста. Чтобы исправить, нужно использовать подготовленные выражения:

$stmt = $conn->prepare("SELECT * FROM products WHERE id = ?");
$stmt->bind_param("i", $_GET['id']);
$stmt->execute();

В этом случае любой ввод будет обработан как число, и инъекция невозможна.

Заключение

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

Источники