一个用于 Cobalt Strike Malleable C2 配置文件的 Python 解释器,允许你以编程方式解析、修改、构建这些配置文件,并进行语法验证。
支持从 Cobalt Strike 4.3 版本开始的所有 Malleable C2 配置文件语法。
该工具不向后兼容早期的 Cobalt Strike 版本。
pyMalleableC2 与其他同类项目有何不同?
if 语句的形式内置了某种“人工智能”。pyMalleableC2 使用 Python 3.9 构建,但应可向后兼容至 Python 3.6。
通过 Pip 安装:
pip3 install pymalleablec2pyMalleableC2 将你视为有能力的成年人,并假定你已掌握编写 Malleable C2 配置文件的方法。它能检测语法错误,但尚未实现运行时检查。如果你指示它生成在实际上无法正常工作的配置文件,它会照做。务必通过 c2lint 测试所生成的配置文件,再投入生产环境!
(从技术上讲,你可以利用此库构建一个 Python 版本的 c2lint,*咳嗽* 欢迎提交 PR *咳嗽*)
pyMalleableC2 的主要作者是 Marcello Salvati
(更多示例请参见 examples 文件夹)
从文件中读取 Malleable C2 配置文件并生成 AST,然后从 AST 重建源代码:
from malleablec2 import Profile
# 根据文件路径解析配置文件
p = Profile.from_file("amazon.profile")
# 打印生成的 AST
print(p.ast.pretty())
# 从 AST 重建源代码并输出到控制台
print(p.reconstruct())
# 以上两步的快捷方式 :)
print(p)
从字符串生成“内联”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()
# 设置 http-get 选项的 uri
http_get.set_option("uri", "/wat/a/tease")
# 创建一个 client 块
client = ClientBlock()
# 在 client 块中添加一条 header 语句
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()
# 设置 http-post 选项的 uri
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)