Что такое ExtractValue и при чём тут SQL-инъекция?
Функция ExtractValue — это встроенная функция MySQL, предназначенная для извлечения значения из XML-документа по заданному XPath-выражению. Синтаксис: ExtractValue(XML_document, XPath_expression). Она часто используется в легитимных задачах обработки XML. Однако злоумышленники нашли способ применять её для проведения SQL-инъекций, основанных на ошибках (error-based SQLi).
Суть атаки: если в XPath-выражение подставить специально сформированную строку, содержащую результат подзапроса, MySQL выдаст сообщение об ошибке, в котором будет содержаться этот результат. Это позволяет извлекать данные из базы, не имея прямого доступа к выводу.
Пример эксплуатации
Рассмотрим классический пример, который часто встречается в отчётах по безопасности и на обучающих платформах (DVWA, SQLZoo).
... AND EXTRACTVALUE(8190, CONCAT(0x7e, (SELECT (ELT(8190=8190,1))), 0x7e)) -- -
Здесь:
8190— произвольное число, играющее роль идентификатора (может быть любым).ELT(8190=8190,1)— функция ELT возвращает N-й элемент из списка. Поскольку условие8190=8190всегда истинно (1), возвращается первый элемент — число 1.CONCAT(0x7e, ..., 0x7e)— объединяет тильды (~) с результатом подзапроса, чтобы отделить его в сообщении об ошибке.
При выполнении такого запроса MySQL попытается обработать XPath-выражение, начинающееся с тильды, что синтаксически недопустимо. Возникнет ошибка вида: XPATH syntax error: '~1~'. В этом сообщении мы видим результат выполнения подзапроса — цифру 1. Если вместо 1 подставить, например, SELECT database(), то в ошибке отобразится имя текущей базы данных.
Почему это работает?
MySQL при обнаружении ошибки в XPath возвращает часть входной строки в тексте ошибки. Злоумышленник может управлять этой строкой через конкатенацию с результатами подзапросов. Таким образом, база данных сама «рассказывает» атакующему конфиденциальную информацию.
Этот метод относится к категории error-based SQL-инъекций и особенно эффективен, когда приложение выводит сообщения об ошибках СУБД на страницу. Даже если вывод ошибок отключён, но они логируются, атакующий может получить данные через слепые методы, но это сложнее.
Какие данные можно извлечь?
С помощью ExtractValue можно получить практически любую информацию, доступную через SQL-запросы:
- Имя текущей базы данных:
SELECT database() - Версию MySQL:
SELECT version() - Имена таблиц:
SELECT table_name FROM information_schema.tables - Логины и пароли пользователей:
SELECT concat(login,0x3a,password) FROM users LIMIT 1
Ограничение — длина выводимой строки в сообщении об ошибке. Обычно она не превышает 64 символов, поэтому для длинных данных применяют посимвольное извлечение или используют другие функции (например, UpdateXML).
Другие функции для error-based инъекций
Помимо ExtractValue, в MySQL существуют аналогичные уязвимые функции:
- UpdateXML — также вызывает ошибку XPath при некорректном выражении.
- GeometryCollection, Polygon, MultiPoint — могут вызывать ошибки формата, содержащие данные.
- Exp — при переполнении выдаёт ошибку с числом.
Все они используются для обхода фильтров, когда ExtractValue заблокирована.
Как защититься от таких атак?
Основной метод — использование параметризованных запросов (prepared statements). Это исключает возможность внедрения SQL-кода, так как данные передаются отдельно от структуры запроса.
Дополнительные меры:
- Отключить вывод подробных ошибок СУБД в production-окружении.
- Использовать ORM или хранимые процедуры с параметрами.
- Проводить валидацию и экранирование всех входных данных, особенно тех, что попадают в SQL.
- Применять WAF для фильтрации подозрительных конструкций (например, содержащих ExtractValue, CONCAT, 0x7e).
- Регулярно обновлять СУБД и фреймворки.
Важно понимать, что экранирование спецсимволов не всегда спасает: в некоторых кодировках возможны обходы. Поэтому параметризация — золотой стандарт.
Пример уязвимого кода на PHP
$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = $id";
$result = mysqli_query($conn, $query);
Если передать в параметр id строку 1 AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e)), то при выводе ошибки мы увидим имя базы. Исправленный вариант:
$stmt = $conn->prepare("SELECT * FROM products WHERE id = ?");
$stmt->bind_param("i", $_GET['id']);
$stmt->execute();
Здесь инъекция невозможна, так как параметр передаётся отдельно.
Заключение
ExtractValue — мощный инструмент в арсенале пентестера, но одновременно и индикатор плохо защищённого приложения. Понимание принципов его работы помогает разработчикам и специалистам по безопасности выявлять и устранять уязвимости. Помните: лучшая защита — это параметризованные запросы и минимизация прав пользователя базы данных.
Комментарии
—Войдите, чтобы оставить комментарий