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

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

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

Когда Вы находитесь в сети с прямым доступом к машинам, на которых могут выполняться Ваши полезные данные, иногда Вам может потребоваться, чтобы они ждали вашего подключения к ним, а не наоборот. В таких случаях полезные данные должны локально привязываться к порту, к которому Вы можете просто подключиться с помощью netcat, чтобы начать взаимодействовать с оболочкой системы.
В полезной нагрузке обратного подключения мы использовали класс TcpClient для создания связи с нападавшим. Здесь вместо использования класса TcpClient, мы будем использовать класс TcpListener для прослушивания соединения злоумышленника, как показано в листинге ниже.

Starting a TcpListener on a given port via command arguments
Starting a TcpListener on a given port via command arguments

Прежде чем начать прослушивание, мы преобразуем аргумент, переданный в полезные данные, в целое число, с помощью int.Parse() [1], которое будет портом для прослушивания. Затем мы создаем экземпляр нового класса TcpListener [2], передавая IPAddress.Any в качестве первого аргумента конструктору и порту, который мы хотим прослушивать, в качестве второго аргумента. Значение IPAddress.Any, переданное в качестве первого аргумента, сообщает TcpListener прослушивать любой доступный интерфейс (0.0.0.0).
Затем мы пытаемся начать прослушивание порта в блоке try/catch. Мы делаем это потому, что вызов Start() [3] может вызвать исключение, если, например, полезная нагрузка запускается не от имени привилегированного пользователя и пытается привязаться к номеру порта меньше 1024 или если он пытается выполнить привязку к порту, к которому уже привязана другая программа. Запустив Start() в блоке try/catch, мы можем перехватить это исключение, и, при необходимости, корректно завершить работу. Конечно, если Start() завершится успешно, полезная нагрузка начнет прослушивать новое соединение на этом порту.


Прием данных, выполнение команд и возврат вывода

Теперь мы можем начать принимать данные от злоумышленника и анализировать команды, как показано в листинге ниже.

Reading the command from the network stream and splitting the command from the arguments
Reading the command from the network stream and splitting the command from the arguments

Чтобы сохранить постоянство целевого объекта, после отключения от полезной нагрузки, мы создаем экземпляр нового класса NetworkStream внутри технически бесконечного цикла while [1], передавая Socket, возвращенный прослушивателем AcceptSocket() [2], в конструктор NetworkStream [3]. Затем, чтобы эффективно прочитать NetworkStream, в контексте оператора using мы создаем экземпляр нового класса StreamReader [4], передавая сетевой поток конструктору StreamReader. После настройки StreamReader мы используем второй бесконечный цикл while [5], чтобы продолжать чтение команд до тех пор, пока злоумышленник не отправит пустую строку в полезные данные.
Чтобы проанализировать и выполнить команды из потока и вернуть выходные данные подключающемуся злоумышленнику, мы объявляем ряд строковых переменных во внутреннем цикле while, и разделяем исходный ввод на любые пробелы в string [6]. Затем мы берем первый элемент из разделения и назначаем его в качестве команды для запуска, используя LINQ для выбора первого элемента в массиве [7]. Затем мы снова используем LINQ, чтобы объединить все строки в разделенном массиве после первого элемента [8], и присвоим полученную строку (с аргументами, разделенными пробелами) переменной arg.


Выполнение команд из потока

Теперь мы можем настроить классы Process и ProcessStartInfo для запуска команды с аргументами, если таковые имеются, и захвата вывода, как показано в листинге ниже.

Running the command, capturing the output, and sending it back to the attacker

Как и в случае с полезной нагрузкой обратного подключения, обсуждавшиеся в предыдущем разделе, для запуска команды, мы создаем экземпляр нового класса Process [1], и назначаем новый класс ProcessStartInfo свойству StartInfo класса Process. Мы устанавливаем имя командного файла для свойства FileName [2] в StartInfo и устанавливаем свойство Arguments [3] с аргументами команды. Затем мы устанавливаем для свойства UseShellExecute значение false, чтобы наш исполняемый файл запускал команду напрямую, и мы устанавливаем для свойства RedirectStandardOutput значение true, чтобы мы могли перехватить выходные данные команды и вернуть их злоумышленнику.
Чтобы запустить команду, мы вызываем метод Start() [4] класса Process. Пока процесс выполняется, мы копируем стандартный поток вывода непосредственно в сетевой поток, отправленный злоумышленнику, передавая его в качестве аргумента CopyTo() [5], а затем ждем завершения процесса. В случае возникновения ошибки мы преобразуем строку «Ошибка выполнения команды » в массив байтов с помощью Encoding.ASCII.GetBytes() [6]. Затем массив байтов записывается в сетевой поток и отправляется злоумышленнику с помощью метода Write() потока [7].
Запуск полезной нагрузки с 4444, в качестве аргумента, заставит прослушиватель начать прослушивание порта 4444 на всех доступных интерфейсах. Теперь мы можем использовать netcat для подключения к прослушивающему порту, как показано в листинге ниже, и начать выполнять команды и возвращать их выходные данные.

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

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