#25 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Привязка полезной нагрузки.
Здравствуйте, дорогие друзья.
Когда Вы находитесь в сети с прямым доступом к машинам, на которых могут выполняться Ваши полезные данные, иногда Вам может потребоваться, чтобы они ждали вашего подключения к ним, а не наоборот. В таких случаях полезные данные должны локально привязываться к порту, к которому Вы можете просто подключиться с помощью netcat, чтобы начать взаимодействовать с оболочкой системы.
В полезной нагрузке обратного подключения мы использовали класс TcpClient для создания связи с нападавшим. Здесь вместо использования класса TcpClient, мы будем использовать класс TcpListener для прослушивания соединения злоумышленника, как показано в листинге ниже.
Прежде чем начать прослушивание, мы преобразуем аргумент, переданный в полезные данные, в целое число, с помощью int.Parse() [1], которое будет портом для прослушивания. Затем мы создаем экземпляр нового класса TcpListener [2], передавая IPAddress.Any в качестве первого аргумента конструктору и порту, который мы хотим прослушивать, в качестве второго аргумента. Значение IPAddress.Any, переданное в качестве первого аргумента, сообщает TcpListener прослушивать любой доступный интерфейс (0.0.0.0).
Затем мы пытаемся начать прослушивание порта в блоке try/catch. Мы делаем это потому, что вызов Start() [3] может вызвать исключение, если, например, полезная нагрузка запускается не от имени привилегированного пользователя и пытается привязаться к номеру порта меньше 1024 или если он пытается выполнить привязку к порту, к которому уже привязана другая программа. Запустив Start() в блоке try/catch, мы можем перехватить это исключение, и, при необходимости, корректно завершить работу. Конечно, если Start() завершится успешно, полезная нагрузка начнет прослушивать новое соединение на этом порту.
Прием данных, выполнение команд и возврат вывода
Теперь мы можем начать принимать данные от злоумышленника и анализировать команды, как показано в листинге ниже.
Чтобы сохранить постоянство целевого объекта, после отключения от полезной нагрузки, мы создаем экземпляр нового класса 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 для запуска команды с аргументами, если таковые имеются, и захвата вывода, как показано в листинге ниже.
Как и в случае с полезной нагрузкой обратного подключения, обсуждавшиеся в предыдущем разделе, для запуска команды, мы создаем экземпляр нового класса 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 для подключения к прослушивающему порту, как показано в листинге ниже, и начать выполнять команды и возвращать их выходные данные.
1 2 3 4 5 |
$ nc 127.0.0.1 4444 whoami timcore uname Linux |
На этом все. Всем хорошего дня!