背景
陆陆续续的也学习Cobaltstrike有一段时间了,学习的目的是为了能干点事情,结合自己的一个工作性质,(笔者是蓝队做防守的),所以想做一些能用到上的东西来,思来想去就是关于cs的反制手段了,同时前段时间沸沸扬扬的cs漏洞也是反制的点,所以有了本文,本文想详细写写关于cs的一些所谓的反制手段。将部分反制手法研究下能落地实现下。
本文主要内容
对抗Cobaltstrike中的手段:
- 1、伪造流量批量上线(欺骗防御)
- 2、利用漏洞(CVE-2022-39197),反制攻击者,获取aggressor 端的相关信息和相关权限
- 3、反制server,爆破密码,获取server信息
- 4、旁路反制
研究分析以及落地实现
一、伪造流量批量上线(欺骗防御)
1、理论研究
场景:
伪造流量批量上线这个手段是属于技战法里面的欺骗防御,通过伪造上线心跳流量,发送至攻击者的c2,使攻击者的c2同时上线的大量终端:
通常有两种常见场景:
第一种是,发现攻击者对我们单位在进行精准钓鱼,那么我们可以通过这个欺骗防御,来混淆攻击者的视听,让攻击者的c2server上线大量机器,但是攻击者却执行不了任何命令。
第二种是,发现我们内部已经存在被钓鱼上线的机器,那么此时我们通过这种欺骗防御就是来拖延攻击者,使攻击者难辨真假,从而争取一些排查处置的时间。
如何伪造上线流量:
这里我们默认大家对cs流量都有基础了解,不了解的可以先看下笔者之前写的关于cs流量的一篇文章:
这里造上线流量,其实就是造心跳流量,cs4.1默认配置的心跳流量如下图:
这里面,其实核心就是伪造cookie字段,这里的cookie字段在cs里面,我们一般叫“元数据”;
这个元数据是个被RSA公钥加密后的内容,所以我们只要能拿到加密使用的公钥和待加密的明文,我们就可以伪造出来这个”元数据“了
公钥我们先不谈,我们先来看看这里的明文一般长什么样,如下就是该明文的组成:
//标志头(4)+Size(4)+Rawkey(16)+字体(4)+beacon ID(4)+ 进程ID(4)+port(2)+内核(4)+09 +失陷IP + 09 + 主机名+ 09 + 用户名+09+进程名
这里我们拿一个例子来看,如下两图,是对一个上述的明文流量的标注:
所以这里我们直接按格式伪造上述流量就可以了,这里有一个比较细节的东西,CS 监听器是怎么来判断一个上线的唯一性的呢,换句话说这里面这么多字段,哪个才是“主键”呢,或者说哪几个一起才是主键呢,我们只要保证n个不同主键,那么就可以伪造n台失陷机器了。
笔者通过实践测试,CS的监听器判断是否是同一个心跳是通过beaconid这个属性来判断,只要这个属性不一样就会被识别成不同的上线流量,感觉cs这里的处理其实不太严谨。
好了这样我们明文流量就可以自己伪造了。
接下来是公钥,拿到公钥就可以对上面伪造的明文流量加密,从而生成cookie字段了。
公钥怎么来呢,我们来分析下:(这个公钥是在首次运行cs server的时候,当没有.cobaltstrike.beacon_keys文件的时候,随机产生的,详情参考)
1、通过一些已知的破解版本里面自带的公钥,可以逐个尝试。
2、攻击者如果是通过stager上线了,即小马拉大马的形式,通过流量设备我们可以获取到拉的beacon,对beacon进行操作,从beacon中可以直接拿到公钥,如下图:
3、攻击者通过stagerless上线,我们需要对样本进行分析,拿到里面的公钥
4、自己做研究的话,可以直接拿到自己cs server端的.cobaltstrike.beacon_keys文件,该文件是RSA密钥对的序列化文件,所以直接从这里可以提出公钥,如下图:
base64解码:
所以到目前为止,我们即有了公钥也有了明文,就可以开始实践了
2、实践落地
笔者这里写了个批量上线的项目:
地址:https://github.com/minhangxiaohui/cobaltstrikefakeup
实践落地:
反序列化还原自己cs server端下的.cobaltstrike.beacon_keys
文件,拿到pubkey:
package ga0wei.text; import sleep.runtime.Scalar; import javax.crypto.Cipher; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; public class Getkey { public static void main(String\[\] args) throws Exception{ PrivateKey privateKey; Cipher cipher; //获取密钥对象 ObjectInputStream var2 \= new ObjectInputStream(new FileInputStream("keys")); Scalar var3 \= (Scalar)var2.readObject(); var2.close(); KeyPair keyPair1 \= (KeyPair) var3.objectValue(); //初始化cipher对象,准备解密 privateKey \=keyPair1.getPrivate(); cipher \=Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(2,privateKey); //元数据 String ciphertext \= "WTbGz2y0K24UJovEX+proVDR+jInn7C/H8JwIa3+DCdm+qkSbquSRU2/n/ss8dMUHogaPFIn/N+xaAOW/gmfvu4HfVNF8Kk/XsRN35By03QWKIbhVbNGyZOBwCYGWs3f9XjUa8rqbiHbmRnRppbEpVv/+pvlVqYUh53bFQP9O7E="; byte\[\] cipherbyte \= Base64.getDecoder().decode(ciphertext); //解密 byte\[\] text \= cipher.doFinal(cipherbyte); //输出文件 和控制台 try(FileOutputStream fos \= new FileOutputStream(new File("Text"))) { System.out.println("解密成功"); fos.write(text); System.out.println(new String(text)); System.out.println("privatekey :"+new String(Base64.getEncoder().encode(keyPair1.getPrivate().getEncoded()))); System.out.println("publickey : "+new String(Base64.getEncoder().encode(keyPair1.getPublic().getEncoded()))); } } }
元数据流量:
元数据格式:标志头(4)+Size(4)+Rawkey(16)+字体(4)+beacon ID(4)+ 进程ID(4)+port(2)+内核(4)+0x09 +失陷IP +0x09 + 主机名+ 0x09 + 用户名+0x09+进程名
伪造元数据并定时发送(CS的心跳流量):
使用上文的cobastrikefakeup.py完成元数据的伪造并发送,实现批量为伪上线:
获取到公钥之后运行脚本效果如下:(其中-T参数是指定上线个数,-U参数是指定心跳回连域名,-P参数是公钥)
Cobastrike server:
二、通过漏洞反制(CVE-2022-39197)
其实cs历史上是有一些漏洞的,比如CVE-2021-36798、CVE-2022-23317之类的,但是感觉都有种,食之无味弃之可惜的意思,所以这里我们通过漏洞反制,主要来看下最近比较火的CVE-2022-39197。
CVE-2022-39197这个漏洞是北辰师傅发现的,9月底cobaltstrike的官网爆出来的,笔者本来以为就是一个xss漏洞,后来看了漂亮鼠师傅的文章之后发现的确这里可扩展的空间很大。接下来我们简单分析下该漏洞,并通过该漏洞来实现一些简单的反制:
漏洞分析:
1、xss
当时这个洞刚爆出来的时候,网上一堆”复现“的截图,如下图(笔者随便找的一张):
看过笔者之前写的cs上线流量文章,或者看完上文“批量上线项目”的实现,我们看到上图其实就会知道这个漏洞的是怎么触发的,上图这里其实就是对user的值进行了伪造,使cs的aggressor端对其进行了渲染从而导致其在对应字段加载了一张图片。
我们先来看看这个xss是怎么实现的:
这里我们还是使用上述的项目,伪造发送元数据(伪造心跳流量),稍微做下改变即可:
将上面“用户名”字段内容,替换成如下的payload,并且我们还要把其他的字段内容缩短:
<html><img src=http://192.168.129.1:8081/Ga0weI.jpg>
如下是对上述cobaltstrikefakeup.py简单修改:可用看到我们缩短了主机名字段和进程名字段来为用户名字段节省空间
这里我们简单讲下为什么要缩短其他字段长度,因为元数据最终的形式是被RSA加密的,RSA是非对称“块加密”算法,其原理是基于单项陷门函数--------大整数难分解,其每次加密的明文长度都是有限制的,这个长度取决于我们在使用RSA时使用的n(也就是那个大整数),在cs中,使用的是n为1024bit,也就是我们常说的128位(这里的位指B)的RSA,所以我们构造的元数据(也就是待加密的明文)是不能超过128位的,又因为cs中使用的是PKCS1Padding的填充方式,这种填充方式占用了11个字节,所以我们真实可用使用的长度其实是128-11=117位。如果读者之前对RSA有过了解的话,其实上述为什么每次加密的明文长度有限制直接可用通过一句话解释:因为在RSA中不管是通过明文加密生成密文,还是通过解密密文生成明文,都是通过模n同余(mod(n))来计算的,所以我们的结果都要受限制于n的长度。
运行改造后的脚本:这里我们-T参数写成1就行:
\-T 1 -U http://192.168.129.132/updates.rss -P MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNUL6+gTcsl1/M1vjCOFsJY2lMm4i5HA4TPki0VH77n57ELBv5H/8pzuWSGtL9n+n+FDiUh4WF84nX6W6dd4Vs8XZEfcbQLpYM10aW0FpVdSVwGxTum9ZilrXMG9UmZOgNtbugwY4eRSxO9ILAnwxXqGbymdSC7VhgSc9E8dNMtQIDAQAB
如下图:user字段是空的,但是其实是我们放入的图片资源
如下图是我们图片资源的请求记录:
这里就形成了xss。
简单看下上面的payload:
<html><img src=http://192.168.129.1:8081/Ga0weI.jpg>
其实这里就是一个在swing中使用HTML标签的方式。
CS没有对元数据中输入的字段进行检查和过滤,从而导致了这个xss的漏洞。
这里我们看下cs的aggressor端怎么获取的user等相关字段的:
首先我们来看看cs 的server端怎么处理从受害端传来的元数据的:
如下是cs4.1中源码,在其beacon.BeaconC2类的process_beacon_metadata
方法是来处理获取到的元数据的:
跟进BeaconEntry类找到对应参数的构造方法:
如下:是获取user字段相关,这里我们注意,实例化BeaconEntry类的时候是没有对传入的字段做过滤检查校验的
我们再来看看前端对JTextField类的赋值的时候:
如下 源码中dialog.DialogManager类的beacon方法中是aggressor端前端swing操作beacon条目的方法:
下图中可用看到在为JTextField对象的时候赋值的时候也没有做检查过滤处理,直接通过BeaconEntry.getUser()方法来获取的。
所以才导致了当我们使用swing的html兼容特性的时候,触发xss漏洞,这里的xss可能有人感觉很鸡肋,但是笔者并不是这样认为,完全可用应用到反制手段中:
场景是:当我们使用该方法造成存储型的xss时,每当有一个aggressor端连接到cs的server端的时候,都会触发请求对应的资源,这样我们可用在溯源的方面得到一些线索,攻击者就暴露了自己公网地址。