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

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

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

В этом разделе мы рассмотрим, как определить полезные нагрузки, которые можно скомпилировать один раз и запустить как в Linux, так и в Windows. Но сначала нам нужно импортировать несколько функций из libc и определить делегат неуправляемой функции Linux, как показано в листинге ниже.

Setting up the payload to run the generated Metasploit payloads
Setting up the payload to run the generated Metasploit payloads

Мы добавляем строки, показанные в листинге выше, в верхнюю часть MainClass рядом с импортом нашей функции Windows. Мы импортируем три функции из libcmprotect(), posix_memalign() и free() — и определяем новый делегат под названием LinuxRun [2]. Он имеет атрибут UnmanagedFunctionPointer, как и наш делегат WindowsRun. Однако вместо передачи CallingConvention.StdCall, как это было в листинге ранее, мы передаем CallingConvention.Cdecl [1], поскольку cdecl — это соглашение о вызовах собственных функций в Unix-подобной среде.
В листинге ниже, мы теперь добавляем оператор else if в наш метод Main(), следующий за оператором if, который проверяет, находимся ли мы на машине Windows.

Detecting the platform and assigning the appropriate payload

Исходное перечисление PlatformID от Microsoft не включало значения для платформ, отличных от Windows. По мере развития Mono были введены неофициальные значения для свойств платформы Unix-подобной системы, поэтому мы проверяем значение Platform непосредственно по значениям магических целых чисел, а не по четко определенным значениям перечисления. Значения 4, 6 и 128 можно использовать, чтобы определить, используем ли мы Unix-подобную систему. Приведение свойства Platform к int позволяет нам сравнить значение Platform с целочисленными значениями 4, 16 и 128.
Как только мы определим, что работаем в Unix-подобной системе, мы можем установить значения, необходимые для выполнения наших собственных полезных нагрузок сборки. В зависимости от нашей текущей архитектуры массиву байтов полезной нагрузки будет назначена либо наша полезная нагрузка x86, либо x86-64.


Выделение памяти

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

Allocating the memory using posix_memalign()
Allocating the memory using posix_memalign()

Сначала мы определяем несколько переменных: ptr, которому с помощью posix_memalign() должен быть присвоен указатель на начало нашей выделенной памяти, если все идет хорошо; успех, которому будет присвоено значение, возвращаемое функцией posix_memalign(), если наше выделение будет успешным; и логическое значение freeMe, которое будет истинным в случае успешного выделения, чтобы мы знали, когда нам нужно освободить выделенную память. (Мы присваиваем freeMe значение false в случае сбоя распределения.)
Затем мы запускаем блок try, чтобы начать выделение, чтобы мы могли перехватить любые исключения и корректно завершить полезную нагрузку в случае возникновения ошибки. Мы установили для новой переменной pagesize значение 4096, что соответствует размеру страницы памяти по умолчанию в большинстве установок Linux.


После назначения новой переменной с именем length, которая содержит длину нашей полезной нагрузки, приведенной к IntPtr, мы вызываем posix_memalign() [1], передавая переменную ptr по ссылке, чтобы posix_memalign() мог изменить значение напрямую, без необходимости передавать его обратно. Мы также передаем выравнивание памяти (всегда кратное 2; 32 — хорошее значение) и объем памяти, который мы хотим выделить. Функция posix_memalign() вернет IntPtr.Zero, если выделение будет успешным, поэтому мы проверяем это. Если IntPtr.Zero не был возвращен, мы печатаем сообщение о сбое posix_memalign(), а затем возвращаемся и выходим из полезных данных. Если выделение прошло успешно, мы изменяем режим выделенной памяти на доступный для чтения, записи и выполнения, как показано в листинге ниже.

Changing the mode of the allocated memory

Примечание
Техника, используемая для выполнения шелл-кода в Linux, не будет работать в операционной системе, которая ограничивает выделение памяти RWX. Например, если в вашем дистрибутиве Linux используется SELinux, эти примеры могут не работать на вашем компьютере. По этой причине я рекомендую Ubuntu — поскольку SELinux отсутствует, примеры должны работать без проблем.


Чтобы в дальнейшем освободить выделенную память, мы устанавливаем для freeMe значение true. Затем мы берем указатель, который posix_memalign() установил во время выделения (переменная ptr), и создаем указатель с выравниванием по страницам, используя пространство памяти, которое мы выделили, выполняя побитовую операцию И над указателем с дополнением до единиц нашего размера страницы [1]. По сути, дополнение единиц фактически превращает адрес нашего указателя в отрицательное число, так что наши математические вычисления для установки прав доступа к памяти складываются.
Из-за того, как Linux распределяет память по страницам, мы должны изменить режим для всей страницы памяти, на которой была распределена наша память для полезных данных. Побитовое И с дополнением единиц к текущему размеру страницы округлит адрес памяти, данный нам posix_memalign(), до начала страницы памяти, где находится указатель. Это позволяет нам установить режим для полной страницы памяти, используемой памятью, выделенной posix_memalign().
Мы также создаем режим для установки памяти, выполняя операцию ИЛИ над значениями 0x04 (чтение), 0x02 (запись) и 0x01 (выполнение) и сохраняя значение из операций OR в переменной режима [2]. Наконец, мы вызываем mprotect() [3], передавая указатель страницы памяти, выравнивание памяти (передаваемое в функцию posix_memalign()) и режим, в который устанавливается память. Как и функция posix_memalign(), IntPtr.Zero возвращается, если mprotect() успешно меняет режим страницы памяти. Если IntPtr.Zero не возвращается, мы печатаем сообщение об ошибке и возвращаемся для выхода из полезных данных.


Копирование и выполнение полезной нагрузки

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

Copying the payload to the allocated memory and executing the payload

Последние несколько строк листинга выше должны выглядеть аналогично коду, который мы написали для выполнения полезных данных Windows. Метод Marshal.Copy() [1] копирует наши полезные данные в буфер выделенной памяти, а метод Marshal.GetDelegateForFunctionPointer() [2] превращает полезные данные в памяти в делегат, который мы можем вызвать из нашего управляемого кода. Как только у нас есть делегат, указывающий на наш код в памяти, мы вызываем его, чтобы выполнить код. Блок finally, следующий за блоком try, освобождает память, выделенную функцией posix_memalign(), если для freeMe установлено значение true [3].
Наконец, мы добавляем сгенерированные нами полезные нагрузки для Windows и Linux к кроссплатформенным полезным нагрузкам, что позволяет нам компилировать и запускать одну и ту же полезную нагрузку как в Windows, так и в Linux.

Заключение


В этом разделе мы обсудили несколько различных способов создания пользовательских полезных данных, которые будут полезны в различных обстоятельствах. Полезные нагрузки, использующие TCP, могут обеспечить преимущества при атаке на сеть: от получения оболочки из внутренней сети до поддержания устойчивости. Используя технику обратного подключения, Вы можете создать оболочку на удаленный ящик, что помогает, например, в фишинговой кампании, когда пентест является полностью внешним по отношению к сети. С другой стороны, техника связывания может помочь Вам поддерживать постоянство на ящиках без необходимости повторно использовать уязвимость на машине, если доступен внутренний доступ к сети.
Полезные данные, которые обмениваются данными через UDP, часто могут обойти плохо сконфигурированные межсетевые экраны и, возможно, смогут обойти систему обнаружения вторжений, ориентированную на TCP-трафик. Несмотря на то, что UDP менее надежен, чем TCP, он предлагает скорость и скрытность, которые обычно не может обеспечить тщательно изученный TCP. Используя полезную нагрузку UDP, которая прослушивает входящие широковещательные сообщения, пытается выполнить отправленные команды, а затем передает Вам результаты, Ваши атаки могут быть немного тише и, возможно, более скрытными за счет стабильности.
Metasploit позволяет злоумышленнику на лету создавать множество типов полезных данных, его легко установить и запустить. Metasploit включает в себя инструмент msfvenom, который создает и кодирует полезные данные для использования в эксплойтах. Используя инструмент msfvenom для создания собственных полезных данных сборки, Вы можете создать небольшой кросс-платформенный исполняемый файл для обнаружения и запуска шелл-кода для различных операционных систем. Это дает вам большую гибкость в использовании полезных данных, которые запускаются на целевом компьютере. Он также использует одну из самых мощных и полезных доступных функций Metasploit.

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

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