#38 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Класс OpenVASSession.
Здравствуйте, дорогие друзья.
Мы будем использовать класс OpenVASSession для связи с OpenVAS. В листинге ниже показаны конструктор и свойства, с которых начинается класс OpenVASSession.
Конструктор OpenVASSession принимает до четырех аргументов: имя пользователя и пароль для аутентификации в OpenVAS (по умолчанию в виртуальном устройстве это admin:admin); хост, к которому необходимо подключиться; и, опционально, порт для подключения на хосте, по умолчанию 9390 [1].
Мы передаем аргумент хоста в IPAddress.Parse() [2] и присваиваем результат свойству ServerIPAddress. Затем мы присваиваем значение переменной порта свойству ServerPort и передаем имя пользователя и пароль методу Authenticate(), если аутентификация прошла успешно (как описано в следующем разделе). Свойства ServerIPAddress и ServerPort назначаются в конструкторе и используются во всем классе.
Свойство Stream использует get [3], чтобы проверить, имеет ли значение частная переменная-член _stream значение null. Если это так, он вызывает GetStream(), который устанавливает [4] _stream с подключением к серверу OpenVAS, а затем возвращает переменную _stream.
Аутентификация на сервере OpenVAS
Чтобы попытаться пройти аутентификацию на сервере OpenVAS, мы отправляем XML-документ с именем пользователя и паролем в OpenVAS, а затем читаем ответ, как показано в листинге ниже. Если аутентификация прошла успешно, мы сможем вызвать команды с более высоким уровнем привилегий, чтобы указать цель для сканирования, получить отчет и т. д.
Метод Authenticate() [1] начинается с принятия двух аргументов: имени пользователя и пароля для аутентификации с помощью OpenVAS. Мы создаем новую XML-команду аутентификации и используем имя пользователя [2] и пароль [3], предоставленные для учетных данных; затем мы отправляем запрос на аутентификацию с помощью ExecuteCommand() [4] и сохраняем ответ, чтобы мы могли убедиться, что аутентификация прошла успешно, и получить токен аутентификации.
Если атрибут status [5] корневого XML-элемента, возвращенный сервером, равен 200, аутентификация прошла успешно. Мы присваиваем методу свойства «Имя пользователя», «Пароль» и любые аргументы, а затем возвращаем ответ аутентификации.
Создание метода для выполнения команд OpenVAS
В листинге ниже показан метод ExecuteCommand(), который принимает произвольную команду OpenVAS, отправляет ее в OpenVAS и затем возвращает результат.
Для выполнения команд с помощью протокола управления OpenVAS мы используем сокет TCP для отправки XML на сервер и получения XML в ответ.
Метод ExecuteCommand() принимает только один аргумент: отправляемый XML-документ. Мы вызываем ToString() для XML-документа, сохраняем результат, а затем используем метод Write() [1] свойства Stream, чтобы записать XML в поток.
Чтение сообщения сервера
Мы используем метод ReadMessage(), показанный в листинге ниже, для чтения сообщения, возвращаемого сервером.
Этот метод считывает документ XML из потока TCP по частям и возвращает документ (или значение NULL) вызывающей стороне. После передачи sslStream [1] в метод мы объявляем MemoryStream [2], который позволяет нам динамически хранить данные, получаемые с сервера. Затем мы объявляем целое число для хранения количества прочитанных байтов и используем цикл do/ while [3], чтобы создать 2048-байтовый буфер для чтения данных. Затем мы вызываем Read() [4] в SslStream, чтобы заполнить буфер количеством байтов, прочитанных из потока, а затем копируем данные, поступающие из OpenVAS, в MemoryStream с помощью Write(), поэтому позже мы сможем проанализировать данные в XML.
Если сервер возвращает меньше данных, чем может содержать буфер, нам нужно проверить, прочитали ли мы действительный XML-документ с сервера. Для этого мы используем GetString() в блоке try/catch [5], чтобы преобразовать байты, хранящиеся в MemoryStream, в анализируемую строку и попытаться проанализировать XML, поскольку синтаксический анализ выдаст исключение, если XML недействителен. Если исключение не выброшено, мы возвращаем XML-документ. Если генерируется исключение, мы знаем, что не закончили чтение потока, поэтому вызываем continue [6], чтобы прочитать больше данных. Если мы закончим чтение байтов из потока и еще не вернули действительный XML-документ, мы возвращаем ноль. Это своего рода защита на случай, если связь с OpenVAS потеряется посередине и мы не сможем прочитать весь ответ API. Возврат значения null позволяет нам позже проверить, действителен ли ответ от OpenVAS, поскольку значение null будет возвращено только в том случае, если мы не сможем прочитать полный ответ XML.
Настройка потока TCP для отправки и получения команд
В листинге ниже показан метод GetStream(), который впервые появляется в листинге выше. Он устанавливает фактическое TCP-соединение с сервером OpenVAS, которое мы будем использовать для отправки и получения команд.
GetStream() настраивает поток TCP для использования в остальной части класса, при взаимодействии с OpenVAS. Для этого мы создаем экземпляр нового TcpClient [1] на сервере, передавая свойства ServerIPAddress и ServerPort в TcpClient, если поток недействителен. Мы оборачиваем поток в SslStream [2], который не будет проверять сертификаты SSL, поскольку сертификаты SSL являются самоподписанными и выдадут ошибку; затем мы выполняем SSL-квитирование, вызвав AuthenticateAsClient() [3]. Поток TCP на сервер OpenVAS теперь может использоваться остальными методами, когда мы начинаем отправлять команды и получать ответы.
Проверка сертификата и сбор мусора
В листинге ниже показаны методы, используемые для проверки сертификатов SSL (поскольку SSL-сертификаты, которые OpenVAS использует по умолчанию, являются самоподписанными) и очистки нашего сеанса после его завершения.
Возвращать true [1], как правило, является плохой практикой, но поскольку в нашем случае OpenVAS использует самозаверяющий сертификат SSL, который в противном случае не будет проверяться, мы должны разрешить все сертификаты. Как и в предыдущих примерах, мы создаем метод Dispose(), чтобы можно было выполнить очистку после работы с сетевыми или файловыми потоками. Если поток в классе OpenVASSession не равен нулю, мы удаляем внутренний поток [2], используемый для связи с OpenVAS.
Получение версии OpenVAS
Теперь мы можем управлять OpenVAS для отправки команд и получения ответов, как показано в листинге ниже. Например, мы можем запускать такие команды, как команда get_version, которая возвращает информацию о версии экземпляра OpenVAS. Позже мы добавим аналогичную функциональность в класс OpenVASManager.
Мы создаем новый OpenVASSession [1], передавая имя пользователя, пароль и хост. Затем мы передаем ExecuteCommand() [2] в XDocument, запрашивая версию OpenVAS, сохраняем результат в новом XDocument, а затем записываем его на экран. Вывод листинга выше должен выглядеть так же, как в листинге ниже.
1 2 3 |
<get_version_response status="200" status_text="OK"> <version>6.0</version> </get_version_response> |
На этом все. Всем хорошего дня!