Gray Hat C#, Nexpose, Программирование

#34 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Автоматизация Nexpose. NexposeSession Class.

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

Мы начнем с написания класса Nexpose Session для взаимодействия с Nexpose API, как показано в листинге ниже.

The beginning of the NexposeSession class with constructor and properties
The beginning of the NexposeSession class with constructor and properties

Конструктор класса NexposeSession [1] принимает до пяти аргументов.
Примечания: три обязательны (имя пользователя, пароль и хост для подключения), а два являются необязательными (порт и версия API со значениями по умолчанию).
3780 [2] и NexposeAPIVersion.v11 [3] соответственно). Начиная с [4],
мы присваиваем свойства Host, Port и APIVersion трем обязательным аргументам. Затем мы отключаем проверку сертификата SSL [5], установив ServerCertificateValidationCallback так, чтобы он всегда возвращал true. Это нарушает хорошие принципы безопасности, но мы отключаем проверку, поскольку Nexpose по умолчанию работает по HTTPS с самозаверяющим сертификатом. (В противном случае проверка сертификата SSL завершится неудачно во время HTTP-запроса.)
Мы [6] пытаемся пройти аутентификацию, вызывая метод Authenticate(), показанный в листинге ниже.

The NexposeSession class’s Authenticate() method

Метод Authenticate() [1] принимает в качестве аргументов имя пользователя и пароль. Чтобы отправить имя пользователя и пароль в API для аутентификации, мы создаем XDocument [2] с корневым узлом LoginRequest и атрибутами идентификатора пользователя и пароля. Мы передаем XDocument методу ExecuteCommand() [3], а затем сохраняем результат, возвращенный сервером Nexpose.Мы [4] определяем, имеет ли XML-ответ Nexpose значение атрибута успеха, равное 1. Если это так [5], мы сначала присваиваем свойству SessionID идентификатору сеанса в ответе и устанавливаем для IsAuthenticated значение true. Наконец, мы возвращаем ответ XML [6].

Метод The ExecuteCommand()

Метод ExecuteCommand(), показанный в листинге ниже, является основой класса NexposeSession.

The beginning of the NexposeSession class’s ExecuteCommand() method

Прежде чем мы сможем отправить данные в Nexpose, нам нужно знать, какую версию API использовать [1], поэтому мы всегда используем блок switch/case (аналогично серии операторов if) для проверки значения APIVersion. Например, значение NexposeAPIVersion.v11 или NexposeAPIVersion.v12 сообщит нам, что нам нужно использовать URI API для версии 1.1 или 1.2.


Выполнение HTTP-запроса к Nexpose API

Определив URI, к которому нужно сделать запрос API, мы можем теперь отправить данные запроса XML в Nexpose, как показано в листинге ниже.

Sending the XML command over HTTP for Nexpose inside ExecuteCommand()
Sending the XML command over HTTP for Nexpose inside ExecuteCommand()

Общение с HTTP API для Nexpose происходит в два этапа. Сначала Nexpose отправляет запрос API с помощью XML, который сообщает Nexpose, какую команду мы выполняем; затем он считывает ответ с результатами запроса API. Чтобы выполнить фактический HTTP-запрос к Nexpose API, мы создаем HttpWebRequest [1] и присваиваем его свойству Method значение POST [2], свойству ContentTypetext/xml [3], а свойству ContentLength — длине нашего XML. Затем мы записываем байты команды API XML в поток HTTP-запросов и отправляем этот поток в Nexpose с помощью Write() [4]. Nexpose проанализирует XML, определит, что делать, а затем вернет результаты в ответ.


Чтение HTTP-ответа из Nexpose API

Далее нам нужно прочитать ответ HTTP на только что сделанный запрос API. В листинге ниже показано, как мы завершаем метод ExecuteCommand(), читая HTTP-ответ от Nexpose и затем возвращая либо XDocument, либо массив необработанных байтов, в зависимости от типа содержимого HTTP-ответа. После завершения метода ExecuteCommand() в листинге ниже мы сможем выполнить запрос API, а затем вернуть правильные данные ответа, в зависимости от типа содержимого ответа.

The last part of the NexposeSession class’s ExecuteCommand() method

Обычно, когда Вы отправляете XML-команду в Nexpose, Вы получаете XML взамен. Но когда вы запрашиваете отчет о сканировании уязвимостей, например Отчет в формате PDF, который мы запросим после выполнения сканирования уязвимостей, Вы получите смешанный HTTP-ответ, а не ответ в формате application/xml. Непонятно, почему именно Nexpose изменяет ответ HTTP на основе отчетов в формате PDF, но поскольку наш запрос может возвращать ответ либо с отчетом в кодировке Base64, либо с XDocument (класс документа XML, который мы впервые использовали в разделе 3), нам необходимо определить, способен ли он обрабатывать оба типа ответов.
Чтобы начать чтение ответа HTTP от Nexpose, мы вызываем GetResponse() [1], чтобы мы могли прочитать поток ответа HTTP; затем мы создаем StreamReader [2] для чтения данных ответа в строку [3] и проверки ее типа содержимого. Если тип ответа — составной/смешанный [4], мы разбиваем ответ на массив строк, чтобы можно было проанализировать данные отчета, используя тот факт, что в составных/смешанных ответах Nexpose всегда используется строка —AxB9sl3299asdjvbA [5] для разделения параметров HTTP, в ответе HTTP.
После разделения HTTP-ответа третий элемент результирующего массива строк всегда будет содержать данные отчета в кодировке Base64, полученные при сканировании. Мы [6] используем две последовательности новой строки (\r\n\r\n) для разделения данных этого отчета. Теперь мы можем ссылаться только на данные в кодировке Base64, но сначала мы должны удалить некоторые недопустимые данные из конца отчета в кодировке Base64. Наконец, мы передаем данные в кодировке Base64 в Convert.FromBase64String() [7], который возвращает массив байтов данных, декодированных в Base64, которые затем можно записать в файловую систему в качестве окончательного отчета в формате PDF для последующего чтения.


Выход из системы и удаление нашей сессии

В листинге ниже показаны методы Logout() и Dispose(), которые упрощают выход из сеанса и очистку всех данных сеанса.

he NexposeSession class’s Dispose() and Logout() methods

В Logout() [1] мы создаем XDocument [2] с корневым узлом LogoutRequest [3] и атрибутом session-id [4]. Когда мы отправляем эту информацию в Nexpose в формате XML, он попытается сделать недействительным токен идентификатора сеанса, фактически выведя нас из системы. В то же время мы устанавливаем для IsAuthenticated [5] значение false, а для SessionID — string.Empty, чтобы очистить старую информацию аутентификации; затем мы возвращаем XML-код ответа на выход из системы.
Мы будем использовать метод Dispose() [6] (требуемый интерфейсом IDisposable) для очистки нашего сеанса Nexpose. Как вы можете видеть в [7], мы проверяем, прошли ли мы аутентификацию, и если да, вызываем Logout(), чтобы сделать наш сеанс недействительным.
В листинге ниже показано, как мы будем использовать NexposeAPIVersion, чтобы определить, какую версию Nexpose API использовать.

The NexposeAPIVersion enum used in the NexposeSession class

Перечисление кода NexposeAPIVersion дает нам простой способ определить, к какому URI API следует отправлять HTTP-запросы. Мы использовали NexposeAPIVersion в листинге ниже, чтобы сделать именно это при создании URI API в ExecuteCommand().


Управление API Nexpose

В листинге ниже показано, как теперь мы можем использовать NexposeSession для связи с Nexpose API, а также для аутентификации и печати SessionID. Это хороший тест, позволяющий убедиться, что написанный нами код работает должным образом.

Using NexposeSession to authenticate with the Nexpose API and print SessionID

Мы [1] пытаемся пройти аутентификацию, передавая имя пользователя, пароль и IP-адрес сервера Nexpose в новый NexposeSession. Если аутентификация прошла успешно, мы отображаем SessionID, присвоенный сеансу, на экране. Если аутентификация не удалась, мы выдаем исключение с сообщением «Ошибка аутентификации».

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

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