Cobalt Strike 默认流量分析

Root
Root
发布于 2023-10-12 / 15 阅读 / 0 评论 / 0 点赞

Cobalt Strike 默认流量分析

1.网络测绘

从网络测绘角度发现cs,也是对cs服务端的探测与识别

1.默认端口50050

弱特征,默认端口为50050,可以在teamserver启动的时候更改image-20230405154041153

2.默认证书

自带证书,规避方式:自定义一个,或者用自己去申请,自定义的也只是假装伪装而已

默认证书

image-20230326162730731

修改证书后

image-20230326162902807

3.profile文件

默认和常见的profile文件

Beacon执行命令的结果默认是通过post请求/submit.php?id=发回给服务器。

IndexURIsIndexURIsIndexURIs
1/ca8/fwlink15/push
2/dpixel9/cm16/ptj
3/__utm.gif10/cx17/j.ad
4/pixel.gif11/pixel18/ga.js
5/g.pixel12/match19/en_US/all.js
6/dot.gif13/visit.js20/activity
7/updates.rss14/load21/IE9CompatViewList.xml

github上开源profile

例如:

https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/normal/amazon.profile

image-20230326173126552

useragent被标记,可以成为辅助判断因素

https://docs.google.com/spreadsheets/d/1bpeziZ-ObG8zKronKGyhXg2UsETk_fqY5dD4Tx0q_eY/edit#gid=1635920259

image-20230326174238548

profile 编写规则

https://unit42.paloaltonetworks.com/cobalt-strike-metadata-encoding-decoding/

https://bbs.kanxue.com/thread-274676.htm#msg_header_h1_1

4.jar指纹

JA3|JA3S/JARM

本来 ja3/ja3s 指纹不算是什么很强的特征,因为它只是一个tls握手包的hash值,ja3s是被动流量,jarm是主动探测,这个指纹取决于你的tls所使用的参数,也就是说完全有可能别人写的https服务也和cs的listener撞上了,但是我们不能不修改,因为放着这样一个特征不去管也会成为可能突破口,修改的办法也很简单,在nanohttpd服务中,修改SSL相关的任意参数就能达到效果。

JA3|JA3S

*总的来说:*ja3 和 ja3s 分别代表 tls 握手阶段的 client-hello、server-hello 的数据集合计算出的哈希值(md5),相同版本相同系统下指纹相同,该特征与操作系统、cobaltstrike 版本有关,profile 文件无法对其修改。

作用:

配合 suricata规则检测CS流量

常见JA3的:

win10-https-beacon-ja3 指纹:72a589da586844d7f0818ce684948eea

image-20230404115409920

Debian-cs4.5-ja3s 指纹:ae4edc6faf64d08308082ad26be60767

image-20230404115523296

JARM

总的来说是一个主动TLS服务端指纹工具,主要用途如下:

  1. 快速验证一组TLS服务器是否使用相同的TLS配置;
  2. 通过TLS配置划分TLS服务器,并识别可能归属的公司;
  3. 识别网站默认的应用或基础架构;
  4. 识别恶意软件C&C控制节点,以及其他恶意服务器。

https://github.com/salesforce/jarm.git

作者测试的c2指纹:

Malicious Server C2JARM FingerprintOverlap with Alexa Top 1M
Trickbot22b22b09b22b22b22b22b22b22b22b352842cd5d6b0278445702035e06875c0
AsyncRAT1dd40d40d00040d1dc1dd40d1dd40d3df2d6a0c2caaa0dc59908f0d36029430
Metasploit07d14d16d21d21d00042d43d000000aa99ce74e2c6d013c745aa52b5cc042d0
Cobalt Strike07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb10
Merlin C229d21b20d29d29d21c41d21b21b41d494e0df9532e75299f15ba73156cee38303

实际测试结果:

image-20230330113730168

发现准确率很低

更换CobaltStrike团队服务器以下指纹测试:

07d14d16d21d21d00042d41d00041d47e4e0ae17960b2a5b4fd6107fbb0926

2ad2ad16d2ad2ad00042d42d00042ddb04deffa1705e2edc44cae1ed24a4da

07d14d16d21d21d00042d43d00041de5fb3038104f457d92ba02e9311512c2

搜索截图:

image-20230330113913491

image-20230330114145492

image-20230330114238790

可以发现相比于作者的指纹,能精准不少。

或者用不同的jdk版本去启动,指纹也会变

https://paper.seebug.org/2046/

5.checksum8

如果使用stager上线,即使用分阶段加载,在第二阶段加载beacon.dll时,会请求cs服务端,算法生成的特征默认为checksum8

checksum8算法:路径的 ascii 十进制之和与 256 取余计算值等于 92(x86)或者93(x64)

image-20230329161657852

使用checksum8校验

image-20230329162009300

nmap脚本https://github.com/whickey-r7/grab_beacon_config

image-20230404122907193

2.流量层面

1.默认证书

keytool -list -v -keystore cobaltstrike.store -storepass 123456

image-20230405155324554

规避方式:用keytool伪造新的证书,推荐使用真实的证书,这里只列举keytool

keytool -keystore cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias google.com -dname "CN=US, OU=google.com, O=Sofaware, L=Somewhere, ST=Cyberspace, C=CN"

image-20230405155200520

2.流量特征

有些特征是弱特征,但是弱特征组合起来就是强特征了

1.下载stage

2.心跳包

  • 请求的uri是cs的默认心跳uri
  • Cookies是一个base64编码的内容
  • 响应头里面的Content-Length字段为0
  • 响应头的Content-Type字段是一个可执行type (application/octet-stream)

3.解密流量

1. 后门区别

Windows Executable (Stage)

生成的 Payload 包含两部分:Stager 和 Stage。Stager 在运行时会向攻击者指定的 IP 和 port 发送 HTTP 请求,以便下载后面的 Stage,然后执行 Stage 中的 Payload 代码。

Windows Executable (s) (Stageless)

生成的 Payload 不再使用 Stage 和 Stager,而是一次性发送所有的 Payload 代码,相当于直接生成一个 stage。

image-20230330144243721

2.已知私钥解密HTTP流量

1. 提取下载stage流量中的公钥

image-20230329174047617

问:团队服务器怎么判断请求是下载stage呢?下载x86还是x64类型的stage?

答:使用checksum8校验访问路径, 92(x86)或者93(x64)

具体通讯截图:

image-20230330142838917

发现强特征/t5En符合checksum8算法

发现弱特征user-agent

  • 将wireshark中抓到的数据包导出
image-20230330162252728 image-20230330162422132 image-20230330162730254

2. 提取元数据

image-20230330170639947
# 写入以下代码 
import java.io.File;
import java.util.Base64;
import common.CommonUtils;
import java.security.KeyPair;

class DumpKeys
{   
    public static void main(String[] args)
    {
        try {
            File file = new File(".cobaltstrike.beacon_keys");
            if (file.exists()) {
                KeyPair keyPair = (KeyPair)CommonUtils.readObject(file, null);
                System.out.printf("Private Key: %s\n\n", new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded())));
                System.out.printf("Public Key: %s\n\n", new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded())));
            }
            else {
                System.out.println("Could not find .cobaltstrike.beacon_keys file");
            }
        }
        catch (Exception exception) {
           System.out.println("Could not read asymmetric keys");
        }
    }
}

java -cp "cobaltstrike.jar" DumpKeys.java

image-20230330163831568
  • 将得到的公钥与stage流量中的公钥进行对比是否一致
image-20230330164244993 image-20230329175853850
  • 心跳包

    默认的Cosbalt Strike 的心跳包中会使用GET请求特定的路径

    作用:

    1.存活反馈

    2.查询是否有任务

    可以发现第二个心跳包返回值长度明显大于其他心跳包,后续又用POST方式提交数据包;明显第二个心跳包返回包中存在任务

    image-20230330220816029
  • 使用Raw key 解密

    https://github.com/minhangxiaohui/CSthing/tree/master/cs-parse-http-traffic

    image-20230330215624859

3.未知私钥解密流量

1. 使用公开的密钥对尝试解密

目前公开的有6对密钥对

https://github.com/minhangxiaohui/CSthing/blob/master/1768_v0_0_8/

对于某主机的测试

image-20230331160841258 image-20230331160925460

2.内存抓取会话密钥

此处使用Cobalt Strike 4.5 版本,Cobalt Strike第4版Beacon,可以从进程内存中恢复未加密的元数据的情况非常罕见。对于这些Beacon,可以采用另一种方法。在可写的进程内存中可以找到AES和HMAC密钥,但没有明确标识这些密钥的标题。它们只是16字节长的序列,没有任何可区分的特征。为了提取这些密钥,该方法包括执行一种字典攻击。在进程内存中发现的所有可能的16字节长的非空序列,将被用来尝试解密一段加密的C2通信。如果解密成功,就说明已经找到了一个有效的密钥。

  • 从wireshark捕获得流量包中提取内容

python3 cs-parse-http-traffic.py -k unknown dump-name

image-20230405160329538
  • 使用procdump导出指定程序内存数据

https://learn.microsoft.com/en-us/sysinternals/downloads/procdump

定期抓取:for /L %x in (1, 1, 1000) do procdump.exe name.exe

  • 尝试解密

python3 cs-extract-key.py -t 上面提取到得加密内容 dump文件

image-20230405161715163

此处并未解密成功

4.分析DNS流量

1. DNS Beacon

在CS4.0版本之前,有两种Beacon:

windows/beacon_dns/reverse_dns_txt
windows/beacon_dns/reverse_http

在CS4.0及以后的版本中,只剩下了一种:

windows/beacon_dns/reverse_dns_txt

2. DNS流量截图:

以下使用的payload:windows/beacon_dns/reverse_dns_txt;生成的beacon为stageless

image-20230403143859817

3.逐条分析

1.image-20230403153347848
  • Beacon发起A查询

465075be表示的Beacon ID(bid)的十六进制表示,每个运行中的Beacon都会产生一个32位的数字,用于在团队服务器中识别Beacon。它对每个运行中的Beacon都是不同的,即使同一个Beacon可执行文件被启动了几次。

通过查询Beacon ID+域名表示这是一个心跳包

  • Team-server返回IPv4 0.0.0.0

表示团队服务器端收到(也就是出现黑窗口,还未激活状态)

2. image-20230403154109691
  • Beacon再次发起A查询
  • Team-server回复返回IPv4 0.0.0.243

DNS_Beacon回复规则:

image-20230404092558617
  1. 如果最后一位为1,意味着beacon要做一个checkin(DNS_metadata查询)。
  2. 如果第4到2位均设置为0,则通过一个A查询来进行通信
  3. 如果第2位为1,则是要通过TXT查询来进行通讯。
  4. 如果第3位为1,则是要通过AAAA查询来进行通讯。

查看DNS_Beacon请求规则

https://github.com/Sentinel-One/CobaltStrikeParser.git

python3 parse_beacon_config.py beacon_name

image-20230403221946699

可以看到最大请求长度是255,进行XOR(异或)操作的值为0.0.0.0(0和任意数xor值不变)

image-20230403212423816

此处0.0.0.243中的243的为二进制11110011和0.0.0.0的二进制进行xor操作后不变即采用0011,符合规则1和规则3,因此客户端应该在后续返回元数据以及任务下发,所以请求的URL为www和api开头

问:为什么运行Beacon时第一个返回的是0.0.0.0,第二个是0.0.0.243

答:DNS beacon上线后需要使用checkin或者其他命令激活,所以第二个为0.0.0.243

3. image-20230403163553594
  • Beacon发起A查询

    域名以www开头,表示这是将把元数据发送到服务器,180为16进制表示长度,03c1225a2中包含了一个计数器和一个随机数字,随机数字为3c1225a2, 计数器是以0开始,增量为1,可以看到此处计数器从0到3,共发送3次数据;位于www和计数器之间的就是加密数据,按照计数器顺序拼接数据:

    33b5f264032cdeead9673be9e57cdba9265a29c39f399e3012ad7623e.438ec8e98fac4410ae0e3b8a75fd419b139a61d2bbdcc8ab0c44b929.e0c0a1ef48be67ae1f9d0e5a433a376d2eaad7b4242225eedd3ee4332b969130cf9ffb3f277158cba4179accacb9c59d0.7d21a925ce77185afa2e2dd0f2ed1a964f47e4dd

  • Team-server回复0.0.0.0 表示收到

4.image-20230403215429267
  • Beacon发起A请求且以api开头

    告知服务端可以发送任务,并且下次查询将采用TXT方式

    计数器+随机数:07d992def

  • Team-server回复0.0.0.48

    表示这个任务长度为48字节,一旦解密后的数据达到这个长度,就会停止发送TXT查询

  • Beacon发起TXT请求

    计数器+随机数:17d992def,表示这是第一个数据包

  • Team-server以TXT方式回复数据

    image-20230404091625514
  • Beacon再次发起A请求(即心跳包)

  • Team-server回复0.0.0.242

    根据上面的规则即0010,所以下次需要发送TXT查询,此次不需要checkin

5.image-20230404094030246
  • Beacon发起A查询以post开头

    post:表示发送命令执行结果

    140:被解码成1和40,40为16进制数据对应10进制为64,1为计数器,所以一个长度为64字节的数据

    042632934:计算器+随机数

    465075be:Beacon ID

  • Team-server回复0.0.0.0表示收到

  • Beacon开始发送加密数据

    发起以下请求数据如下:

    20736b4c0c8f977522e0ca89653e3d39d9d0f22c137a57ea5e5c5ebf0.02a4592f3f01839010c5ddce052e952c2c10f3260ca0931b0170e643.142632934.465075be

    第一个2表示此处有两个加密标签即:

    0736b4c0c8f977522e0ca89653e3d39d9d0f22c137a57ea5e5c5ebf0

    02a4592f3f01839010c5ddce052e952c2c10f3260ca0931b0170e643

    142632934:计算器+随机数

    465075be:Beacon ID

  • Team-sever回复0.0.0.0表示收到(此处由于数据太长无法显示)

  • Beacon再次发送加密数据

    13ea8c3632874d913.242632934

    第一个1表示只有以一个加密标签即:

    3ea8c3632874d913

    242632934:计数器+随机数

  • 所以拼接的加密数据为

    0736b4c0c8f977522e0ca89653e3d39d9d0f22c137a57ea5e5c5ebf002a4592f3f01839010c5ddce052e952c2c10f3260ca0931b0170e6433ea8c3632874d913

    长度为128的16进制数据,即64字节(一个16进制位等于0.5个字节)

  • Team-sever回复0.0.0.0表示收到

4. 流量分析与解密参考文章链接