Exploit, Gray Hat C#, SQL-Injection, SQL-Injection (Blind)

#16 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Использование логических слепых SQL-уязвимостей. Метод MakeRequest().

Здравствуйте, дорогие друзья.

Мы почти готовы запустить наш эксплойт, за исключением одного: нам нужен способ отправлять полезные данные внутри циклов for. Для этого нам нужно написать метод MakeRequest(), который принимает единственный аргумент: передаваемую полезную нагрузку (см. листинг ниже).

The MakeRequest() method sending the payload and returning the server’s response

Мы создаем базовый запрос GET HttpWebRequest [2], используя полезную нагрузку и URL-адрес [1] экземпляра BadStore. Затем, используя StreamReaderw [3], мы читаем ответ в строку и возвращаем ответ вызывающей стороне. Теперь мы запускаем эксплойт и должны получить результат, подобный показанному в ниже.

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


Получение длин значений

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

Retrieving the length of certain values in the database

Метод GetLength() принимает два аргумента: строку базы данных, из которой нужно получить значение [1], и столбец базы данных, в котором будет находиться это значение [2]. Мы используем цикл for (см. листинг ниже), чтобы получить длину строк в таблице userdb. Но в отличие от предыдущих полезных данных SQL мы используем функцию CHAR_LENGTH() [3] вместо LENGTH, поскольку извлекаемые строки могут быть 16-битным Unicode вместо 8-битного ASCII. Мы также используем предложение LIMIT [4], чтобы указать, что мы хотим получить значение из определенной строки, возвращаемой из полной таблицы пользователей. После получения длины значения в базе данных, мы можем получить фактическое значение побайтно, как показано в листинге ниже.

The second loop within the GetLength() method retrieving the actual length of the value

Как Вы можете видеть в листинге выше, мы создаем общий List [1], чтобы хранить значения, полученные с помощью полезных данных, чтобы мы могли преобразовать их в целые числа и вернуть их вызывающему объекту. Перебирая длину счетчика, мы отправляем HTTP-запросы для проверки байтов значения с помощью MakeRequest() [2], и полезных данных SQL-инъекции. Если ответ содержит ошибку «круглые скобки не сбалансированы» [3], мы знаем, что наша полезная нагрузка SQL оценена как true. Это означает, что нам нужно сохранить значение c (символ, который соответствует i) в виде байта [4], чтобы мы могли преобразовать List в удобочитаемую строку. Поскольку мы нашли текущий символ, нам больше не нужно проверять заданный индекс счетчика, поэтому мы прерываемся, чтобы перейти к следующему индексу.
Теперь нам нужно вернуть счетчик и завершить метод, как показано в листинге ниже.

The final line in the GetLength() method, converting the value for the length into an integer and returning it

Получив байты счетчика, мы можем использовать GetString() [2], чтобы преобразовать собранные байты в удобочитаемую строку. Эта строка передается в int.Parse() [1], и возвращается вызывающей стороне, чтобы мы могли начать сбор фактических значений из базы данных.


Написание GetValue() для получения заданного значения

Мы завершаем этот эксплойт методом GetValue(), как показано в листинге ниже.

The GetValue() method, which will retrieve the value of a given column at a given row

Методу GetValue() требуются три аргумента: строка базы данных, из которой мы извлекаем данные [1], столбец базы данных, в котором находится значение [2], и длина значения, которое необходимо извлечь из базы данных [3]. Создан новый экземпляр List [4] для хранения байтов собранного значения.
В самом внутреннем цикле [5] мы повторяем от 32 до 126, поскольку 32 — это наименьшее целое число, соответствующее печатному символу ASCII, а 126 — наибольшее. Ранее, при получении значений мы выполняли итерацию только от 48 до 58, поскольку нас интересовали только числовые символы ASCII.
Перебирая эти значения, мы отправляем полезную нагрузку, сравнивающую текущий индекс значения в базе данных с текущим значением итерации внутреннего цикла for. Когда ответ возвращается, мы ищем ошибку «скобки не сбалансированы» [6] и, если она найдена, приводим значение текущей внутренней итерации к байту и сохраняем его в списке байтов. Последняя строка метода преобразует этот список в строку с помощью GetString() [7] и возвращает новую строку вызывающему объекту.


Вызов методов и печать значений

Все, что теперь осталось, — это вызвать новые методы GetLength() и GetValue() в нашем методе Main() и распечатать значения, полученные из базы данных. Как показано в листинге ниже, мы добавляем цикл for, вызывающий методы GetLength() и GetValue(), в конец нашего метода Main(), чтобы можно было извлечь адреса электронной почты и хэши паролей из базы данных.

The for loop added to the Main() method, which consumes the GetLength() and GetValue() methods

Для каждой строки таблицы userdb, мы сначала получаем длину [1] и значение [2] поля электронной почты, а затем значение поля passwd (хэш MD5 пароля пользователя). Затем мы печатаем длину поля и его значение, получая результаты, подобные показанным в листинге ниже.

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

Резюме
В этом разделе Вы познакомились с фаззингом и использованием уязвимостей XSS и SQL-инъекций. Как Вы видели, BadStore содержит множество SQL-инъекций, XSS и других уязвимостей, каждая из которых может быть использована немного по-разному. В этом разделе мы реализовали небольшое приложение для фаззинга запросов GET для поиска в параметрах строки запроса XSS или ошибок, которые могут указывать на существование уязвимости, связанной с внедрением SQL.
Благодаря полному и гибкому классу HttpWebRequest для создания и получения HTTP-запросов и ответов, мы смогли определить, что параметр searchquery при поиске элементов в BadStore уязвим как для XSS, так и для SQL-инъекций.
После того, как мы написали простой фаззер GET-запросов, мы перехватили HTTP-запрос POST от BadStore, используя HTTP-прокси Burp Suite и Firefox, чтобы написать небольшое приложение для фаззинга для POST-запросов. Используя те же классы, что и в предыдущем фаззере запросов GET, но с некоторыми новыми методами, мы смогли найти еще больше уязвимостей SQL-инъекций, которые можно было бы использовать.
Далее мы перешли к более сложным запросам, например HTTP-запросам с JSON. Используя уязвимое веб-приложение JSON, мы перехватили запрос, используемый для создания новых пользователей в веб-приложении с помощью Burp Suite. Чтобы эффективно фаззинг HTTP-запросов этого типа, мы представили библиотеку Json.NET, которая упрощает анализ и использование данных JSON.
Наконец, когда Вы хорошо поняли, как фаззеры могут находить возможные уязвимости в веб-приложениях, Вы научились их эксплуатировать. Снова воспользовавшись BadStore, мы написали эксплойт SQL-инъекции на основе UNION, который мог извлекать хэши имен пользователей и паролей из базы данных BadStore с помощью одного HTTP-запроса. Чтобы эффективно извлекать извлеченные данные из HTML-кода, возвращаемого сервером, мы использовали классы регулярных выражений Regex, Match и MatchCollection.
Как только более простой эксплойт UNION был завершен, мы написали слепую SQL-инъекцию на основе логического значения для того же HTTP-запроса. Используя класс HttpWebRequest, мы определили, какие из HTTP-ответов были истинными или ложными, на основе полезных данных SQL-инъекций, передаваемых в веб-приложение. Когда мы узнали, как веб-приложение будет вести себя в ответ на вопросы «верно-неверно», мы начали задавать базе данных вопросы «верно-неверно», чтобы получить из нее информацию по одному байту за раз. Слепой эксплойт на основе логического значения сложнее, чем эксплойт UNION, и требует больше времени и HTTP-запросов для выполнения, но он особенно полезен, когда UNION невозможен.

На этом все. Всем хорошего дня!

Цикл статей по Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности.