Техника эксплуатации UNION в SQL

«UNION — это базовый оператор SQL для объединения запросов. Но в контексте инъекций он превращается в отмычку, которая позволяет не просто сломать дверь, а проскользнуть внутрь и вынести данные, не нарушая работу приложения. Понимание этой техники — это не просто знание синтаксиса, а осознание логики уязвимости, где легитимная функция становится вектором атаки, потому что приложение слепо доверяет данным.»

Как работает оператор UNION в SQL

Оператор UNION — это стандартная инструкция языка SQL, предназначенная для вертикального объединения результирующих наборов нескольких запросов SELECT. Его задача — сформировать единую таблицу результатов из разных источников данных. В отличие от JOIN, который соединяет таблицы по горизонтали (добавляя колонки), UNION складывает строки.

Базовый синтаксис выглядит так:

SELECT column_a, column_b FROM table_one
UNION
SELECT column_x, column_y FROM table_two;

Для корректной работы UNION требуется соблюдение ключевого условия: количество и, что критически важно, совместимые типы данных столбцов в каждом из объединяемых запросов должны быть одинаковыми. Первый запрос определяет имена столбцов в итоговом наборе.

Важный нюанс: По умолчанию UNION автоматически удаляет дубликаты строк из конечного результата. Если необходимо сохранить все строки, включая повторяющиеся, используется модификатор UNION ALL. С точки зрения производительности UNION ALL часто эффективнее, так как не требует дополнительной операции устранения дублей.

От легитимного инструмента к вектору атаки

В рутине администрирования или разработки отчетов UNION — обыденный инструмент. Однако в контексте уязвимости SQL Injection (SQLi) он кардинально меняет свою роль. Если злоумышленник может внедрить произвольный SQL-код в выполняемый запрос, UNION становится самым прямым способом извлечения данных.

Цель классической инъекции через UNION — не сломать логику приложения (хотя это возможно), а «дописать» к исходному, вероятно безобидному запросу, свой собственный. В результате приложение, получая данные, например, о товаре по ID, параллельно и незаметно для себя выбирает и отдает злоумышленнику содержимое таблиц с учетными записями, платежами или системными настройками.

Пример сценария атаки:

-- Оригинальный запрос приложения, например:
SELECT product_name, price FROM products WHERE id = USER_INPUT;

-- Если USER_INPUT контролируется атакующим и подставляется как:
'1' UNION SELECT username, password_hash FROM users --

-- Итоговый запрос к БД примет вид:
SELECT product_name, price FROM products WHERE id = '1'
UNION
SELECT username, password_hash FROM users --';

Приложение выполнит оба запроса и может отобразить имена товаров вместе с логинами и хешами паролей на той же самой странице.

Сборка работающего UNION-запроса: тактика разведки

Проведение успешной UNION-атаки — это не слепое вставление кода, а методичный процесс, напоминающий обратную разработку. Атакующему необходимо решить несколько задач вслепую, без доступа к исходному коду приложения и схеме базы данных.

1. Определение количества столбцов

Первый и обязательный шаг — выяснить, сколько столбцов возвращает оригинальный запрос приложения. Для этого используется метод подбора с ORDER BY или UNION SELECT с последовательностью значений.

Метод ORDER BY: Инструкция ORDER BY 1 сортирует результат по первому столбцу, ORDER BY 2 — по второму и так далее. Атакующий увеличивает номер столбца до тех пор, пока запрос не вернет ошибку (например, «Unknown column ‘5’ in ‘order clause'»). Последний успешный номер и будет искомым количеством столбцов.

http://vuln-site.com/page?id=1' ORDER BY 5--

Метод UNION SELECT NULL: Более прямой способ. Атакующий подставляет UNION SELECT NULL, NULL, NULL..., добавляя значения NULL до тех пор, пока ошибка о несоответствии количества столбцов не исчезнет.

http://vuln-site.com/page?id=1' UNION SELECT NULL--               (ошибка)
http://vuln-site.com/page?id=1' UNION SELECT NULL,NULL--          (ошибка)
http://vuln-site.com/page?id=1' UNION SELECT NULL,NULL,NULL--     (успех, значит столбцов 3)

2. Поиск уязвимых столбцов

Не все столбцы в запросе могут быть пригодны для вывода данных на страницу. Некоторые могут обрабатываться внутренней логикой и не отображаться. После определения количества столбцов нужно выяснить, какие из них «видны» — то есть их содержимое попадает в HTTP-ответ.

Для этого в каждый столбец по очереди подставляется легко идентифицируемое значение (например, строка ‘test’ или число 12345).

http://vuln-site.com/page?id=1' UNION SELECT 'test1',NULL,NULL--
http://vuln-site.com/page?id=1' UNION SELECT NULL,'test2',NULL--
http://vuln-site.com/page?id=1' UNION SELECT NULL,NULL,'test3'--

Найдя в ответе сервера строки ‘test1’, ‘test2’ или ‘test3’, атакующий понимает, какие позиции в запросе можно использовать для утечки данных.

3. Определение типов данных

Наконец, необходимо убедиться, что данные, которые планируется извлечь (например, строки из колонки password), могут быть помещены в выбранные уязвимые столбцы. Если столбец ожидает числовой тип, а в него попытаться вставить текст, произойдет ошибка. Поэтому финальная проверка — подстановка в целевые столбцы значений разных типов (строк, чисел, дат) для подтверждения их совместимости.

Полноценная эксплуатация: от структуры к данным

После завершения разведки атакующий обладает всей необходимой информацией для построения финального запроса. Теперь можно приступить непосредственно к извлечению данных, начиная с мета-информации о самой базе данных.

Большинство СУБД (MySQL, PostgreSQL, Microsoft SQL Server) имеют специальные системные таблицы или представления (information_schema, sys.tables и т.д.), в которых хранится информация обо всех таблицах, столбцах и их типах. Первый запрос в цепочке эксплуатации часто направлен именно туда.

Типичная последовательность этапов UNION SQL-инъекции
-- Пример для MySQL: получение списка таблиц в текущей базе данных
' UNION SELECT table_name, NULL FROM information_schema.tables --

Узнав имена таблиц (например, users, payments, config), атакующий запрашивает структуру наиболее интересной из них:

-- Получение списка столбцов в таблице 'users'
' UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name='users' --

И, наконец, финальный аккорд — извлечение чувствительных данных:

-- Извлечение логинов и хешей паролей
' UNION SELECT login, password_hash FROM users --

Оборона: почему параметризованные запросы — не панацея

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

Сложный случай: динамические идентификаторы объектов. Параметризовать можно значения, но не имена таблиц или столбцов. Если приложение, например, строит запрос вида SELECT * FROM ? WHERE id = ?, где имя таблицы подставляется динамически, параметризация первого параметра невозможна — это вызовет синтаксическую ошибку. В таких сценариях необходимо использовать «белые списки» (whitelisting) допустимых значений, строго валидируя их на стороне сервера.

Полноценная защита строится на многослойном подходе:

Уровень защиты Меры и инструменты Что предотвращает
Разработка Параметризованные запросы, строгая валидация по белому списку, ORM с встроенной защитой, принцип минимальных привилегий для учетных записей БД. Непосредственное внедрение кода, включая UNION.
Конфигурация Отключение детализированных ошибок СУБД в production-среде (чтобы не помогать атакующему в разведке), регулярное обновление СУБД и middleware. Утечку информации об ошибках, эксплуатацию известных уязвимостей в СУБД.
Мониторинг и реагирование WAF (Web Application Firewall) с сигнатурами на SQLi, анализ логов приложений и БД на аномальные шаблоны запросов (множественные запросы с UNION, ORDER BY). Автоматизированные атаки, обнаружение успешных инцидентов.
Тестирование Регулярный ручной и автоматизированный пентест (статический и динамический анализ), особенно параметров, влияющих на структуру запроса. Выявление уязвимостей до эксплуатации.

Важно помнить: UNION-инъекция — это не самостоятельная уязвимость, а лишь метод эксплуатации базовой уязвимости SQL Injection. Блокировка самого оператора UNION на уровне WAF — полумера. Опытный тестировщик или злоумышленник может использовать альтернативные техники, такие как слепая (blind) или основанная на ошибках (error-based) инъекция. Поэтому фокус защиты должен быть смещен на устранение коренной причины — некорректной обработки непроверенных пользовательских данных в контексте SQL-запросов.

Оставьте комментарий