实战场景中 Cobalstrike 的二次开发

渗透技巧 2年前 (2022) admin
28 0 0

前言

    随着近几年 HW 实战的开展所有人都对 Cobalstrike 不再陌生,相关安防设备一直在思考如何在流量侧 / 终端侧识别出 CS 的行踪。攻击者除了使用习惯上基于插件进行开发提效外,还有一些需要关注对抗的点来提升 CS 的隐藏性,我们会分两篇文章将 CS 在流量层面、内存层面对抗侧二次开发做出的思考实践与大家分享交流。

流量层面的思考

从流量整体伪装的角度来看,最好的效果就是和正常流量混为一体,以此为出发点来构思流量隐蔽方案。

  1. 有些思路会说改变CS原有的加密方式,自定义编码/加密的算法,不局限于C2 profile里面的几种算法。不管是哪种算法,我觉得目前基于HTTPS流量的通信方式是最好的,对于中间流量设备来说所观测到的https流量也是最多的,我们的c2混在其中让其无法区分。

  2. 当然即使是完全加密的流量,也能使用其他方式进行特征抽象。比如 SPLUNK 和 RITA(一款用于网络流量分析的开源框架)都曾实践过对通信频率,发送/接收平均字节数,连接数进行分析,使用数学的方式提取检测特征。当然我们也可以做出应对,增加流量发送的随机性,对于Beacon在静默状态和使用状态设置不同的通信频率,达到在时序性上的对抗。

在实践中我们更关注第一点。传统的域前置方式利用HTTPs隐藏真实的请求Host,来绕过安全设备的流量检测。从中间设备观察到的DNS和TLS sni均是可信域名。但随着云服务厂商进一步限制,域前置的使用范围进一步缩小。我们使用了Shadow-TLS这一方式,不再依赖于云厂商的实现缺陷,也能达到隐蔽流量的效果。

Cobalt Strike 流程概述

cs.auth 认证流程

cs 的 license 核心是非对称加密算法,对于关键的 beacon 相关文件使用 rsa 公钥加密,license 文件则是包含了 cobalt stirke 版本、watermark 标志、license 过期时间以及 beacon 解密私钥等信息。

主函数

客户端主函数 agressor, server 端主函数 server.TeamServer,其中客户端主要是启动之前校验一下 license,然后是启动图形界面,数据初始化。客户端这块不是主要关注的点,我们主要关注的点在服务端。也就是 TeamServer 类。

TeamServer 类也继承于 Starter 启动器父类,也会检测 license 合法性,获取过期时间水印等参数,解析 c2profile 配置文件,拉起 server 服务、listener,等待客户端连接上来,其中 listener 是由 nanohttpd 服务承载。

beacon 的生成

当选择一种生成模式之后从通用模板 artifact.exe 当中填充 beacon.dll 的 shellcode,profile 配置也是在这个时候经过 xor 加密填充到预留的空间。

beacon 的上线流程

收集一波主机信息之后向 server 发送一个上线包,此时产生一个 session id 作为这台肉鸡的标识,后续则不停发送心跳包(结合随机垃圾数据以免成为固定特征)等待下一次指令。

第一步明显特征规避

二阶段加载特征

二开第一步先把常规的 cs 特征规避了,像是 profile 配置、端口证书这些都不需要代码层面的改动,github 上比较火的开源 profile 配置也已经被检测为特征了。我们更关注的是需要代码改动的特征,cs 的 stage 二阶段远程加载需要下载 beacon 的 shellcode,当访问 url 路径符合 checksum8 计算的值,listener 就会返回二阶段所需要运行的真正执行 beacon 的 shellcode。使用二阶段加载 beacon 的功能会增加资产测绘被发现的风险,而且 beacon 的一阶段也不具备什么特殊的功能,不太符合实际场景的需求,大家可能更倾向于 stageless 或者定制化二阶段加载。这里的做法是直接删除该服务,毕竟怎么也不会用上单一的二阶段加载 beacon。

ja3/ja3s | jarm 指纹修改

JA3 是由 John Althouse、Jeff Atkinson 和 Josh Atkins 创建的开源项目。JA3/JA3S 可以为客户端和服务器之间的通信创建 SSL 指纹。唯一签名可以表示从 Client Hello 数据包中的字段收集的几个值:

- SSL Version
- Accepted Ciphers
- List of Extensions
- Elliptic Curves
- Elliptic Curve Formats

JA3 工具用于为与服务器的客户端信标连接生成签名。另一方面,JA3S 能够生成服务器指纹。两者的结合可以产生最准确的结果。JA3S 可以捕获完整的加密通信并结合 JA3 的发现来生成签名。更详细的信息可以直接查看官方 github 仓库:https://github.com/salesforce/ja3。

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

实战场景中 Cobalstrike 的二次开发

这里列出几个已知的 ja3/ja3s 指纹信息:

JA3

  • 72a589da586844d7f0818ce684948eea
  • a0e9f5d64349fb13191bc781f81f42e1

JA3s

  • b742b407517bac9536a77a7b0fee28e9
  • ae4edc6faf64d08308082ad26be60767

可以看到修改过后的值:实战场景中 Cobalstrike 的二次开发

Sigma 检测规则

根据开源的 sigma 检测规则,上面公开了一些关于 cs beacon 的特征,像是 cs 默认证书以及检测被公开的一些 profile 配置,这里提几个实战中可能遇到的:

  • 查看 DNS 日志基于 dns_stager_subhost 的默认 DNS c2 行为
实战场景中 Cobalstrike 的二次开发
  • 基于对单个域的大量 DNS 查询检测
实战场景中 Cobalstrike 的二次开发
  • 还有一分钟内从单一来源查找多条 TXT 记录
实战场景中 Cobalstrike 的二次开发

如果实战中遇到只有 DNS 出网的上线需求的话可以考虑使用 DNS beacon, 注意一下默认的 DNS 行为就行,使得 DNS 请求趋于正常流量,根据配置就可以做到。只是需要注意一下一些通用的检测手段。

第二步隐蔽通信流量

接下来是本次二开 CS 中的重点,传统的 CDN 域前置隐藏 c2 的方式大家早已熟知了,CDN 厂商也做了限制手段,想要再做出提升,唯有另辟蹊径,因此我们将目标对准了 Shadow-TLS,以此达到更好的隐蔽流量的效果。

一般使用域前置时,把 CDN 作为白名单域名,当作一个靶子,中间设备所观测到的是一直与 CDN 通信:实战场景中 Cobalstrike 的二次开发

现在 CDN 厂商使用应对手段禁止域前置被恶意使用:

  1. 对比 SNI 和 Host 是否相等,如果是 HTTPS 流量需要解密
  2. 禁止未验证的域名加速

传统 CDN 域前置遇到了些许阻碍,但有另一种思路,shadow-tls 将任意白名单域名作为影子域名,借用 TLS 连接建立起伪装,这或许是个不错的选择。

在讲 shadow-tls 之前我们先简单回顾下 TLS 握手的流程:

实战场景中 Cobalstrike 的二次开发

客户端发送握手,服务端返回握手、证书,双方协商密钥,之后统一用一个密钥对称加密之后的内容。那么怎么在这上面做文章呢?shadowtls 的实现方式:

实战场景中 Cobalstrike 的二次开发

客户端请求与中间人服务器建立连接,握手阶段服务端将客户端的请求转发到⼀个可信域名上(顾名思义 Shadow 域名),这样保证流量侧看到的服务端证书是⼀个可信域名的证书,在握手结束后,client 和 server 切换模式,利用已建立的连接传输自定义数据即可。这样做其实相当于一次 “TLS 表演”,给中间设备看的。并且在 client Response 中使用预定共享 key 返回 8 字节的 hmac 值,用于区分客户端流量和主动探测流量,正确响应主动探测流量。

snowc2 的实现方式

我们如何在 CS 中实现呢?这里我们用了更为直接的办法,在接收到影子域名的 server hello 握手包之后替换掉证书部分,使用 c2 server 的证书协商出密钥完成握手,接着就是正常的 https 流量 application data,同样给中间设备做了一次 “TLS 表演” 。

对于 CS Server 来说想要实现这部分功能,需要覆盖 JDK 类,这里用到了 java 提供的 endorsed 技术,可以简单理解为 -Djava.endorsed.dirs 指定的目录面放置的 jar 文件,将有覆盖系统 API 的功能。我们找到 CertificateMessage.java 文件中 T12CertificateProducer 类的 onProduceCertificate 方法,这个方法是处理 tls 握手时返回 sever hello 消息中的证书数据部分的,我们在其发送证书信息之前,替换成白名单域名的证书。这里给出演示 DEMO,实际项目实现需完成更多细节。

实战场景中 Cobalstrike 的二次开发

既然服务端使用了白名单域名证书,客户端也要写一个替换证书,只不过要替换成原来的证书,写一个简单判断即可。

实战场景中 Cobalstrike 的二次开发

在 tls 握手阶段,判断 server hello 消息头,识别出证书消息,将其替换为我们原来的自签名证书。

最终效果

当我们自建域前置并结合 shadow tls 时:

client -> tls -> cdn -> c2 server
        tls 请求
        SNI: allow.com 
        http 请求
        Host: allow.com

中间设备所观测到的将会是,客户端向 allow.com 发起了请求,经过 TLS 握手进行后续的通信,证书也是 allow.com,从明面上来讲,已经找不出毛病。

实战场景中 Cobalstrike 的二次开发
client hello sni
实战场景中 Cobalstrike 的二次开发
server certificate

可以看到客户端和 cs server 的 tls 握手通信变成了与 www.baidu.com 域名证书的握手。此外除了 https 连接请求之外,我们也加入了 dns 请求的流量,以让其看起来更像正常的通信流量。

这次二开我们专注在 C2 流量层面所做的事就告一段落,后续会更新内存层面的去特征化实践,请大家继续关注雪诺凛冬实验室。

Reference

  • https://www.ihcblog.com/a-better-tls-obfs-proxy/
  • https://thedfirreport.com/2022/01/24/cobalt-strike-a-defenders-guide-part-2/
  • https://github.com/salesforce/ja3
  • https://github.com/snowtech-cn/shadow-tls-client
  • https://github.com/snowtech-cn/serverhelloEndorsed

关于雪诺凛冬实验室

实战场景中 Cobalstrike 的二次开发

原文始发于微信公众号(雪诺凛冬实验室):实战场景中 Cobalstrike 的二次开发

版权声明:admin 发表于 2022年12月30日 下午4:52。
转载请注明:实战场景中 Cobalstrike 的二次开发 | CTF导航

相关文章