
Python interpreter for Cobalt Strike Malleable C2 Profiles. Allows you to parse, build and modify them programmatically.
Интерпретатор Python для профилей Malleable C2 от Cobalt Strike, который позволяет программно разбирать, изменять, создавать их и проверять синтаксис.
Поддерживает всю грамматику профилей Malleable C2 от Cobalt Strike, начиная с версии 4.3.
Он не обратно совместим с предыдущими версиями Cobalt Strike.
В чем различия между pyMalleableC2 и другими проектами подобного рода?
if.pyMalleableC2 был создан с использованием Python 3.9, но должен быть обратно совместим вплоть до Python 3.6.
Установка через Pip:
pip3 install pymalleablec2pyMalleableC2 считает вас взрослым человеком и предполагает, что вы умеете писать профили Malleable C2. Он способен обнаруживать синтаксические ошибки, но проверки времени выполнения не реализованы. Он с радостью сгенерирует профили, которые на самом деле не будут работать в производственной среде, если ему так указать. Всегда прогоняйте сгенерированные профили через c2lint перед использованием в production!
(Технически вы могли бы создать Python-версию c2lint с помощью этой библиотеки, **кхм* PR приветствуются *кхм*)
Основной автор pyMalleableC2 — Marcello Salvati
Twitter: @byt3bl33d3r, Github: @byt3bl33d3r
(Больше примеров в папке examples)
Генерация AST для профиля Malleable C2, расположенного в файле, и восстановление исходного кода из AST:
from malleablec2 import Profile
# Разбор профиля по его пути
p = Profile.from_file("amazon.profile")
# Вывод сгенерированного AST
print(p.ast.pretty())
# Восстановление исходного кода из AST и вывод в консоль
print(p.reconstruct())
# Сокращенный вариант для вышеуказанного :)
print(p)
Генерация AST для «встроенного» профиля Malleable C2 и восстановление исходного кода из AST:
code = '''
set jitter "0";
set sleeptime "3000";
http-get {
set uri "/wow/this/is/cool";
}
http-post {
set uri "/pymalleablec2/is/the/shit";
}
'''
# Разбор профиля из строки
p = Profile.from_string(code)
# Вывод сгенерированного AST
print(p.ast.pretty())
# Восстановление исходного кода из AST и вывод в консоль
print(p)
Программное создание профиля Malleable C2 с нуля:
from malleablec2 import Profile
from malleablec2.components import *
# Создание пустого профиля
p = Profile.from_scratch()
# Установка некоторых глобальных опций
p.set_option("sleeptime", "0")
p.set_option("jitter", "0")
p.set_option("pipename", "mojo__##")
# Создание блока http-get
http_get = HttpGetBlock()
# Установка опции uri для http-get
http_get.set_option("uri", "/wat/a/tease")
# Создание блока client
client = ClientBlock()
# Добавление оператора header в блок client
client.add_statement("header", "Accept", "*/*")
# Создание блока server
server = ServerBlock()
# Добавление блоков client и server в блок http-get
http_get.add_code_block(client)
http_get.add_code_block(server)
# Создание блока http-post
http_post = HttpPostBlock()
# Установка опции uri для http-post
http_post.set_option("uri", "/wat/ucraycray")
# Добавление блоков http-get и http-post в профиль
p.add_code_block(http_get)
p.add_code_block(http_post)
# Восстановление исходного кода из сгенерированного AST и вывод в консоль
print(p)
Очень простой пример, показывающий, как программно рандомизировать профиль Malleable C2:
from malleablec2 import Profile
from malleablec2.randomizer import ProfileRandomizer
from lark import Token
class MyRandomizer(ProfileRandomizer):
# Реализуем метод global_option_set, который будет вызываться для каждого разобранного оператора глобальной опции в профиле
def global_option_set(self, tree):
option_name = tree.children[0]
if option_name == "pipename":
# «Рандомизируем» значение pipename
tree.children[1].children[0] = Token('ESCAPED_STRING', '"my_random_pipename_##"')
# Разбор профиля по его пути
p = Profile.from_file("amazon.profile")
r = MyRandomizer()
# Проход по сгенерированному AST профиля и применение правил рандомизации
r.randomize(p)
# Восстановление исходного кода и вывод профиля в консоль
print(p)