Введение
В мире веб-безопасности SQL-инъекции остаются одной из самых распространённых и опасных уязвимостей. Среди них особое место занимают error-based инъекции, которые позволяют злоумышленнику извлекать данные из базы данных, вызывая контролируемые ошибки в СУБД. Строка ") AND EXTRACTVALUE(8942,CONCAT(0x7e,((SELECT (ELT(8942=8942,1)))),0x7e))-- - — это яркий пример такой атаки, нацеленной на MySQL. В этой статье мы подробно разберём, как она работает, какие функции задействованы и как защититься от подобных угроз.
Что такое error-based SQL-инъекция?
Error-based SQL-инъекция — это метод эксплуатации уязвимости, при котором атакующий заставляет базу данных выдать сообщение об ошибке, содержащее полезные данные. В отличие от blind-инъекций, где информация извлекается по косвенным признакам (например, по времени отклика), здесь результат виден непосредственно в тексте ошибки, возвращаемом приложением.
В MySQL для этих целей часто используются функции EXTRACTVALUE и UPDATEXML, которые генерируют ошибки формата XPath при передаче некорректных аргументов. Эти ошибки содержат переданную строку, что и позволяет вытаскивать произвольные данные.
Разбор строки по частям
Рассмотрим анализируемую строку детально:
") AND EXTRACTVALUE(8942,CONCAT(0x7e,((SELECT (ELT(8942=8942,1)))),0x7e))-- -
Она состоит из нескольких логических блоков:
- ") — закрывающая кавычка и скобка. Это попытка завершить ранее открытый строковый литерал и условие в оригинальном SQL-запросе, чтобы внедрить свой код.
- AND — логический оператор, присоединяющий вредоносное условие к исходному запросу.
- EXTRACTVALUE(8942, CONCAT(...)) — вызов функции, которая извлекает значение из XML-фрагмента по XPath-выражению. Первый аргумент (8942) — произвольное число, которое будет частью сообщения об ошибке. Второй аргумент — конкатенация тильды (0x7e) с полезной нагрузкой и ещё одной тильды.
- CONCAT(0x7e, ((SELECT (ELT(8942=8942,1)))), 0x7e) — функция CONCAT объединяет символ тильды (код 0x7e), результат подзапроса и снова тильду. Тильды служат маркерами, чтобы в сообщении об ошибке легко было выделить извлечённые данные.
- SELECT (ELT(8942=8942,1)) — это подзапрос, который возвращает 1, если условие 8942=8942 истинно (что всегда так), и NULL в противном случае. Функция ELT(N, str1, str2, ...) возвращает N-ю строку из списка. Здесь N — результат сравнения 8942=8942 (1 — истина), а список состоит из одного элемента — 1. Таким образом, подзапрос вернёт 1. В реальной атаке вместо этого подзапроса мог бы быть, например, SELECT version() или SELECT table_name FROM information_schema.tables.
- -- - — комментарий, который отсекает оставшуюся часть оригинального запроса, чтобы не нарушать синтаксис.
Как работает EXTRACTVALUE в контексте инъекции
Функция EXTRACTVALUE в MySQL предназначена для извлечения значения из XML-строки по заданному XPath-выражению. Синтаксис: EXTRACTVALUE(XML_frag, XPath_expr). Если XPath-выражение некорректно, MySQL возвращает ошибку вида:
XPATH syntax error: '...'
Внутри кавычек оказывается переданное XPath-выражение. Злоумышленник использует это, чтобы поместить туда результат подзапроса. В нашем случае XPath-выражение — это CONCAT(0x7e, (подзапрос), 0x7e). Поскольку тильда (~) не является допустимым символом в начале XPath, MySQL сгенерирует ошибку, включив в неё всю строку, например:
XPATH syntax error: '~1~'
Таким образом, атакующий видит результат выполнения подзапроса (в данном примере — 1) в сообщении об ошибке.
Зачем нужны тильды и число 8942?
Число 8942 в первом аргументе EXTRACTVALUE — это просто произвольный идентификатор, который также попадёт в сообщение об ошибке, помогая отличить одну инъекцию от другой. Тильды (0x7e) служат ограничителями, чтобы извлечённые данные было легко найти в тексте ошибки. Без них результат подзапроса мог бы слиться с другим текстом.
Подзапрос SELECT (ELT(8942=8942,1)) в данном примере — это демонстрация. В реальной атаке вместо него подставляются запросы к системным таблицам, например:
SELECT version()— узнать версию MySQL.SELECT database()— узнать имя текущей базы данных.SELECT table_name FROM information_schema.tables LIMIT 1— получить имя первой таблицы.SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 1— получить имя столбца.SELECT password FROM users LIMIT 1— извлечь пароль пользователя.
Поскольку в сообщении об ошибке можно вывести только ограниченное количество символов (обычно до 32), атакующие используют функции SUBSTRING или MID для посимвольного извлечения длинных данных.
Пример уязвимого запроса
Предположим, на сервере выполняется следующий SQL-запрос, где пользовательский ввод подставляется без экранирования:
$query = "SELECT * FROM articles WHERE id = '" . $_GET['id'] . "'";
Если злоумышленник передаст в параметре id строку ") AND EXTRACTVALUE(8942,CONCAT(0x7e,((SELECT (ELT(8942=8942,1)))),0x7e))-- -, то итоговый запрос станет таким:
SELECT * FROM articles WHERE id = '") AND EXTRACTVALUE(8942,CONCAT(0x7e,((SELECT (ELT(8942=8942,1)))),0x7e))-- -'
После подстановки MySQL выполнит вредоносное условие и вернёт ошибку с результатом. Если приложение выводит ошибки СУБД на экран, атакующий получит данные.
Последствия и защита
Успешная error-based SQL-инъекция может привести к полному раскрытию базы данных: злоумышленник получит доступ к логинам, паролям, персональным данным пользователей, коммерческой тайне. В некоторых случаях возможна модификация или удаление данных.
Для защиты необходимо:
- Использовать подготовленные выражения (prepared statements) с параметризацией запросов. Это самый надёжный способ, так как данные не смешиваются с кодом SQL.
- Применять хранимые процедуры с типизированными параметрами.
- Экранировать специальные символы с помощью функций вроде mysqli_real_escape_string, но это менее надёжно, чем подготовленные выражения.
- Отключить вывод ошибок СУБД в production-окружении. Ошибки должны логироваться, но не показываться пользователю.
- Использовать WAF (Web Application Firewall) для фильтрации подозрительных запросов, хотя это не заменяет исправление уязвимости.
- Регулярно обновлять СУБД и фреймворки, чтобы закрыть известные уязвимости.
- Проводить аудит безопасности и пентесты для выявления подобных дыр.
Заключение
Строка ") AND EXTRACTVALUE(8942,CONCAT(0x7e,((SELECT (ELT(8942=8942,1)))),0x7e))-- - — это не случайный набор символов, а тщательно сконструированная полезная нагрузка для error-based SQL-инъекции в MySQL. Она демонстрирует, как с помощью функций EXTRACTVALUE, CONCAT и ELT можно заставить базу данных выдать свои секреты через сообщение об ошибке. Понимание механизмов таких атак помогает разработчикам и администраторам лучше защищать свои приложения. Помните: безопасность — это процесс, а не одноразовое действие.
Комментарии
—Войдите, чтобы оставить комментарий