#30 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Класс NessusSession.
Здравствуйте, дорогие друзья.
Чтобы автоматизировать отправку команд и получение ответов от Nessus, мы создадим сеанс с классом NessusSession и выполним команды API, как показано в листинге ниже.
Как Вы можете видеть в листинге выше, этот класс реализует интерфейс IDisposable [1], поэтому мы можем использовать класс NessusSession внутри оператора using. Как Вы, возможно, помните из предыдущих глав, интерфейс IDisposable позволяет нам автоматически очищать наш сеанс с Nessus путем вызова Dispose(), который мы вскоре реализуем, когда текущий экземпляр класса в инструкции using удаляется во время сборки мусора.
Кроме того [3], мы присваиваем свойству Host значение параметра хоста, переданного конструктору NessusSession [2], а затем пытаемся выполнить аутентификацию [4], поскольку для любых последующих вызовов API потребуется аутентифицированный сеанс. Если аутентификация не удалась, мы создаем исключение и печатаем предупреждение «Ошибка аутентификации». Если аутентификация прошла успешно, мы сохраняем ключ API для дальнейшего использования.
В методе Authenticate() [5] мы создаем JObject [6] для хранения учетных данных, передаваемых в качестве аргументов. Мы будем использовать их для попытки аутентификации, а затем вызовем метод MakeRequest() [7] (обсуждается далее) и передадим метод HTTP, URI целевого хоста и JObject. Если аутентификация прошла успешно, MakeRequest() должен вернуть JObject с токеном аутентификации; если аутентификация не удалась, он должен вернуть пустой JObject.
Когда мы получаем токен аутентификации, мы присваиваем его значение свойству Token [8], присваиваем свойству Authenticated значение true и возвращаем true вызывающему методу, чтобы сообщить программисту, что аутентификация прошла успешно. Если аутентификация не удалась, мы возвращаем false.
Выполнение HTTP-запросов
Метод MakeRequest() выполняет фактические HTTP-запросы, а затем возвращает ответы, как показано в листинге ниже.
Метод MakeRequest() имеет два обязательных параметра (HTTP и URI) и два необязательных (JObject и токен аутентификации). Значение по умолчанию для каждого равно нулю.
Чтобы создать MakeRequest(), мы сначала создаем базовый URL-адрес для вызовов API [2], объединяя параметры хоста и URI и передавая результат в качестве второго аргумента; затем мы используем HttpWebRequest для создания HTTP-запроса и устанавливаем для свойства метода HttpWebRequest [3] значение переменной метода, переданной в метод MakeRequest(). Затем мы проверяем, предоставил ли пользователь токен аутентификации в JObject. В этом случае мы присваиваем заголовку HTTP-запроса X-Cookie значение параметра [4] токена, который Nessus будет искать при аутентификации. Мы устанавливаем для свойства ContentType [5] HTTP-запроса значение application/json, чтобы гарантировать, что сервер API знает, как обращаться с данными, которые мы отправляем в теле запроса (в противном случае он откажется принять запрос).
Если JObject передается в MakeRequest() в третьем аргументе [1], мы преобразуем его в массив байтов с помощью GetBytes() [6], поскольку метод Write() может записывать только байты. Мы присваиваем свойству ContentLength размер массива, а затем используем Write() [7], чтобы записать JSON в поток запросов. Если JObject, переданный в MakeRequest(), имеет значение null, мы просто присваиваем ContentLength значение 0 и двигаемся дальше, поскольку мы не будем помещать какие-либо данные в тело запроса.
Объявив пустую строку для хранения ответа от сервера, мы начинаем блок try/catch по адресу [8], чтобы получить ответ. В инструкции using мы создаем StreamReader [9] для чтения HTTP-ответа, передавая поток HTTP-ответа сервера конструктору StreamReader; затем мы вызываем ReadToEnd(), чтобы прочитать полное тело ответа в нашу пустую строку. Если чтение ответа вызывает исключение, мы можем ожидать, что тело ответа пусто, поэтому мы перехватываем исключение и возвращаем пустой JObject в ReadToEnd(). В противном случае мы передаем ответ в Parse() [10] и возвращаем полученный JObject.
Выход из системы и очистка
Чтобы завершить работу над классом NessusSession, мы создадим LogOut() для выхода из сервера и Dispose() для реализации интерфейса IDisposable, как показано в листинге ниже.
Метод LogOut() [1] проверяет, прошли ли мы аутентификацию на сервере Nessus. Если да, мы вызываем MakeRequest(), передавая DELETE в качестве метода HTTP; /session как URI; и токен аутентификации, который отправляет HTTP-запрос DELETE на сервер Nessus, фактически отключая нас. После завершения запроса мы устанавливаем для свойства Authenticated значение false. Чтобы реализовать интерфейс IDisposable, мы создаем Dispose() [2] для выхода из системы, если мы прошли аутентификацию.
Тестирование класса NessusSession
Мы можем легко протестировать класс NessusSession с помощью небольшого метода Main(), как показано в листинге ниже.
В методе Main() [1] мы создаем новый NessusSession [3] и передаем IP-адрес хоста Nessus, имя пользователя и пароль Nessus в качестве аргументов. В сеансе с аутентификацией мы печатаем токен аутентификации [4], который Nessus предоставил нам при успешной аутентификации, а затем выходим.
Примечание
NessusSession создается в контексте оператора using [2], поэтому метод Dispose(), который мы реализовали в классе NessusSession, будет автоматически вызываться по завершении блока using. Это приведет к выходу из сеанса NessusSession, что сделает недействительным токен аутентификации, который нам предоставил Nessus.
Запуск этого кода должен вывести токен аутентификации, аналогичный показанному в листинге ниже.
1 2 |
$ mono ./ch5_automating_nessus.exe Your authentication token is: 19daad2f2fca99b2a2d48febb2424966a99727c19252966a $ |
На этом все. Всем хорошего дня!