Это вторая по счету статься из цикла про SQL инъекции. В данном статье мы с вами рассмотрим особенности SQL инъекций при использовании команды UNION.

Так для начала разберемся, что делает команда UNION.

Ключевое слово UNION позволяет выполнить один или несколько дополнительных запросов SELECT и добавить их результаты к исходному запросу. Например:

SELECT a, b FROM table1 UNION SELECT c, d FROM
table2

Этот SQL-запрос вернет один набор результатов с двумя столбцами, содержащий значения из столбцов a и b в таблице 1 и столбцов c и d в таблице 2. Важно помнить, что:

1.     UNION должен возвращать одинаковое количество столбцов

2.     Типы данных в каждом столбце должны быть совместимы между отдельными запросами.

Чтобы осуществить атаку SQL-инъекции UNION, необходимо убедиться, что ваша атака отвечает этим двум требованиям. Обычно для этого необходимо выяснить сколько столбцов возвращается из исходного запроса.

При выполнении атаки UNION в SQL-инъекции существует два эффективных метода определения количества столбцов, возвращаемых из исходного запроса.

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

' ORDER BY 1--

' ORDER BY 2--

' ORDER BY 3--

и т.д.

Эта серия полезных нагрузок изменяет исходный запрос, чтобы упорядочить результаты по различным столбцам в наборе результатов. Столбец в предложении ORDER BY может быть указан по его индексу, поэтому вам не нужно знать имена столбцов. Если указанный индекс столбца превышает количество фактических столбцов в наборе результатов, база данных выдает ошибку, например:

Позиция ORDER BY с номером 3 находится вне диапазона количества элементов в списке выбора, так как уже ничего не возвращает. Это потолок.

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

Второй метод заключается в передаче серии запросов UNION SELECT, в которых указывается разное количество нулевых значений:

' UNION SELECT NULL--

' UNION SELECT NULL,NULL--

' UNION SELECT NULL,NULL,NULL--

и т.д.

 Если количество нулевых значений не совпадает с количеством столбцов, база данных возвращает ошибку.

Опять же, приложение может действительно вернуть это сообщение об ошибке, а может просто вернуть общую ошибку или отсутствие результатов. Когда количество нулей совпадает с количеством столбцов, база данных возвращает дополнительную строку в наборе результатов, содержащую нулевые значения в каждом столбце. Влияние на результирующий ответ HTTP зависит от кода приложения. Если вам повезет, вы увидите в ответе какое-то дополнительное содержимое, например, дополнительную строку в HTML-таблице. В противном случае нулевые значения могут вызвать другую ошибку, например, NullPointerException. В худшем случае ответ может быть неотличим от того, который вызван неправильным количеством нулей, что делает этот метод определения количества столбцов неэффективным.

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

 В Oracle каждый запрос SELECT должен использовать ключевое слово FROM и указывать допустимую таблицу. В Oracle существует встроенная таблица под названием dual, которая может быть использована для этой цели. Таким образом, инжектированные запросы в Oracle должны выглядеть следующим образом:

' UNION SELECT NULL FROM DUAL-.

Описанные полезные нагрузки используют последовательность комментариев с двойным тире --, чтобы закомментировать остаток исходного запроса после точки инъекции. В MySQL за последовательностью двойного тире должен следовать пробел. В качестве альтернативы для идентификации комментария можно использовать хэш-символ #.

Поиск столбцов с полезным типом данных в атаке UNION при SQL-инъекции

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

Определив количество необходимых столбцов, вы можете проверить каждый столбец на возможность хранения строковых данных, выполнив серию запросов UNION SELECT, которые помещают строковое значение в каждый столбец по очереди. Например, если запрос возвращает четыре столбца, вы должны отправить:

' UNION SELECT 'a',NULL,NULL,NULL--.

' UNION SELECT NULL,'a',NULL,NULL--

' UNION SELECT NULL,NULL,'a',NULL--

' UNION SELECT NULL,NULL,NULL,'a'--.

 Если тип данных столбца не совместим со строковыми данными, инжектированный запрос вызовет ошибку базы данных, например:

При преобразовании значения varchar 'a' в тип данных int произошел сбой преобразования.

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

Использование SQL-инъекции UNION для получения полезных данных

Предположим, что:

1.      Исходный запрос возвращает два столбца, оба из которых могут содержать строковые данные.

2.      Точкой введения является строка в кавычках в предложении WHERE.

3.      База данных содержит таблицу users с колонками username и password.

 

В этой ситуации вы можете получить содержимое таблицы users, выполнив ввод:

' UNION SELECT username, password FROM users--

Конечно, решающей информацией, необходимой для выполнения этой атаки, является то, что существует таблица users с двумя столбцами username и password. Без этой информации вам пришлось бы пытаться угадать имена таблиц и столбцов. На самом деле, все современные базы данных предоставляют возможность исследовать структуру базы данных, чтобы определить, какие таблицы и столбцы она содержит.

Извлечение нескольких значений в одном столбце

В предыдущем примере предположим, что запрос возвращает только один столбец.

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

' UNION SELECT username || '~' || password FROM users--

В этом случае используется последовательность двойного разделителя ||, который является оператором конкатенации строк в Oracle. Введенный запрос объединяет значения полей имя пользователя и пароль, разделенные символом ~.

Результаты запроса позволят вам прочитать все имена пользователей и пароли, например:

administrator~admin

user1~pass1

user2~pass2

Обратите внимание, что разные базы данных используют разный синтаксис для выполнения конкатенации строк.

Это была 2 часть из цикла статей про SQL-инъекции. Совсем скоро выйдет часть 3.

Комментарии (5)


  1. FanatPHP
    02.04.2023 06:37
    +5

    Поскольку вы забыли указать, что "данный статья" — это перевод, я вам помогу, поставив ссылку на оригинал: https://portswigger.net/web-security/sql-injection/union-attacks


  1. mbait
    02.04.2023 06:37

    Скажите, в 2023-ем действительно остались программисты, которые не используют параметризованные SQL запросы?


    1. FanatPHP
      02.04.2023 06:37

      Разумеется. Достаточно зайти на сайт qna.habr.com и убедиться, что они никуда не делись. Особенно умно в этом плане устроен Питон, где последовательность %s используется и в качестве параметра для форматированных строк, и в качестве параметра для SQL запросов. Нулевой шанс перепутать же.


      Кроме того, есть пограничные случаи, когда кроме данных надо подставить в запрос какой-то другой элемент, и многие путаются, как поступать в таких случаях.


      Но соглашусь, что сам по себе подобный формат статей довольно бессмысленный. Причем дважды.
      Во-первых, вы правы в том, что защита от инъекций в сотни раз проще, чем все эти хитроумные варианты эксплуатации. И если следовать этим простым правилам защиты, то вся статья тут же превращается в тыкву — никакие ухищрения с UNION не помогут.
      А во-вторых, по сути эти статьи являются таким ликбезом по SQL для чайников. В которых банальные элементы SQL выдаются за сокровенные знания. "Оооо, в SQL, оказывается, есть UNION! Оооо, оказывается, с его помощью можно читать данные из двух таблиц!" Фактически, умение эксплуатировать инъекции сводится к владению SQL. Эксплуатация инъекций — это творчество, а не тупое зазубривание нескольких приёмчиков. Скажем, если в запросе даже есть инъекция, но при этом используется ORDER BY, то все эти примитивные рецепты снова превращаются в тыкву.


    1. yri066
      02.04.2023 06:37

      Да, ещё есть сайты, в которых можно встроить SQL инъекцию, сайт моего универа с оценками страдает этим, но всем на это пофиг и никто не собирается это исправлять.


  1. MyraJKee
    02.04.2023 06:37

    Да уж. Лет 20 назад товарищ с помощью sql инъекций вытащил из бд провайдера данные по карточкам с кодами для активации и-нета.

    Использовал их и его конечно же нашли ))) взяли на работу, а ущерб заставили возместить.