目录导航
分析Cobalt Strike 威胁情报分析之Cobalt Strike
我不确定今年会发生什么,但是从现在开始,从APT41到APT32,Cobalt Strike似乎是全球使用最广泛的恶意软件,甚至上一次SolarWinds供应链攻击都涉及Cobalt Strike。在不重新发布有关发布攻击性工具的激烈辩论的情况下,此博客文章旨在总结分析人员需要了解的有关Cobalt Strike的知识,以便在事件发生时快速进行识别和分析。
查找Cobalt Strike服务器
几个月前,Salesforce安全团队发布了一个名为JARM的新的活动指纹工具。它等效于他们去年发布的JA3。它基于远程服务器的TLS配置(例如TLS版本或TLS扩展)生成指纹,而无需考虑证书。识别某些工具使用的自定义Web服务器特别有用,Cobalt Strike就是其中之一。
这是Cobalt Strike JARM签名:
07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1
JARM已经添加到Shodan,BinaryEdge和SecurityTrails中,Shodan最近在上添加了索引ssl.jarm
,因此很容易在野外找到Cobalt Strike服务器。
我们来Shodan上找答案
ssl.jarm:07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1:

Shodan使用此JARM指纹Cobalt Strike服务器确定了5623个IP,主要是在Amazon和Digital Ocean上。如果限制为端口443,则将获得3423个IP。
我们可以使用JARM轻松确认Cobalt Strike仍在第一个IP的端口443上运行:
$ python jarm.py 78.152.61.71
Domain: 78.152.61.71
Resolved IP: 78.152.61.71
JARM: 07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1
(此JARM签名实际上是Java Web服务器的签名,并且特定于JAVA 11堆栈,因此它包括与Cobalt Strike不相关的其他工具(例如Burp Suite),并且不包括使用不同Java版本的CobaltStrike。
获取Cobalt Strike的payloads
Cobalt Strike使用称为checksum8的算法使用url的校验和来提供32b或64b版本的有效负载(与metasploit服务器相同)。Cobalt Strike的反编译代码已在GitHub或其他地方发布了几次,它提供了有关此校验和的信息:
public static long checksum8(String text) {
if (text.length() < 4) {
return 0L;
}
text = text.replace("/", "");
long sum = 0L;
for (int x = 0; x < text.length(); x++) {
sum += text.charAt(x);
}
return sum % 256L;
}
public static boolean isStager(String uri) {
return (checksum8(uri) == 92L);
}
public static boolean isStagerX64(String uri) {
return (checksum8(uri) == 93L && uri.matches("/[A-Za-z0-9]{4}"));
}
我们可以轻松地通过暴力破解算法来找到与python相匹配的网址:
from itertools import product
import string
def checksum8(strr):
j = 0
if len(strr) < 4:
return 0
strr = strr.replace("/", "")
for c in strr:
j += ord(c)
return j % 256
chars = string.ascii_letters + string.digits
to_attempt = product(chars, repeat=4)
for attempt in to_attempt:
word = ''.join(attempt)
r = checksum8(word)
if r == 92:
print("{:30} - 32b checksum".format(word))
elif r == 93:
print("{:30} - 64b checksum".format(word))
$ python bf_checksum8.py
aaa9 - 32b checksum
aab8 - 32b checksum
aab9 - 64b checksum
aac7 - 32b checksum
aac8 - 64b checksum
aad6 - 32b checksum
aad7 - 64b checksum
[...]
因此/aaa9
应返回32位信标(如果可用),并/aab9
应返回64位信标(如果可用)。让我们测试一下Shodan列表中的其中一台Cobalt Strike服务器103.39.18.184
(AS136800-ICIDC NETWORK-中国)(要知道的一件事是Cobalt Strike服务器阻止了异常的用户代理)。
$ wget --no-check-certificate https://103.39.18.184/aaa9
--2020-12-19 17:44:32-- https://103.39.18.184/aaa9
Connecting to 103.39.18.184:443... connected.
WARNING: cannot verify 103.39.18.184's certificate, issued by ‘CN=gmail.com,OU=Google Mail,O=Google GMail,L=Mountain View,ST=CA,C=US’:
Self-signed certificate encountered.
WARNING: certificate common name ‘gmail.com’ doesn't match requested host name ‘103.39.18.184’.
HTTP request sent, awaiting response... 404 Not Found
2020-12-19 17:44:33 ERROR 404: Not Found.
$ wget --no-check-certificate --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" https://103.39.18.184/aaa9
--2020-12-19 17:44:52-- https://103.39.18.184/aaa9
Connecting to 103.39.18.184:443... connected.
WARNING: cannot verify 103.39.18.184's certificate, issued by ‘CN=gmail.com,OU=Google Mail,O=Google GMail,L=Mountain View,ST=CA,C=US’:
Self-signed certificate encountered.
WARNING: certificate common name ‘gmail.com’ doesn't match requested host name ‘103.39.18.184’.
HTTP request sent, awaiting response... 200 OK
Length: 208980 (204K) [application/octet-stream]
Saving to: ‘aaa9’
aaa9 100%[============================>] 204.08K 655KB/s in 0.3s
2020-12-19 17:44:53 (655 KB/s) - ‘aaa9’ saved [208980/208980]
$ wget --no-check-certificate --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36" https://103.39.18.184/aab9
--2020-12-19 17:44:58-- https://103.39.18.184/aab9
Connecting to 103.39.18.184:443... connected.
WARNING: cannot verify 103.39.18.184's certificate, issued by ‘CN=gmail.com,OU=Google Mail,O=Google GMail,L=Mountain View,ST=CA,C=US’:
Self-signed certificate encountered.
WARNING: certificate common name ‘gmail.com’ doesn't match requested host name ‘103.39.18.184’.
HTTP request sent, awaiting response... 200 OK
Length: 260679 (255K) [application/octet-stream]
Saving to: ‘aab9’
aab9 100%[============================>] 254.57K 872KB/s in 0.3s
2020-12-19 17:44:59 (872 KB/s) - ‘aab9’ saved [260679/260679]
因此,该IP103.39.18.184
给了我们两个Cobalt Strike信标:
- 742a06efbebca717271b6beda1ff4a22f6f0be6acda9590ab32b38e1d5721140 aaa9(32b)
- 04bf2657dedfc99235220f59d3e7284d9e2ef0a183cd90ee3514137481a27d6c aab9(64b)


解密Cobalt Strike信标
服务器返回的文件实际上不是PE文件:
$ file *
aaa9: data
aab9: data
在执行Cobalt Strike漏洞利用期间,它将下载此信标并直接在内存中运行。因此,此文件是一小段数据,在开头包含shellcode,该shellcode解码并执行信标。
我们可以使用miasm轻松地绘制此shellcode:

第一个JMP在加密密钥和加密信标之前直接进入呼叫。调用返回到Shellcode,下一个POP EDX
获得密钥的地址。中的代码loc_f
获取EBX中的密钥,EAX中有效负载的长度,并将其存储在堆栈上最终信标的地址中。循环loc_1d
进入信标,然后用密钥对其进行异或。
我们可以轻松地在python中重现它,挑战是找到基址。这loc_47
是紧接密钥,长度和加密有效负载之前的调用地址,因此基址为0x47 + 5(调用指令的长度)。基地址随有效负载的不同而变化,但是可以通过搜索最后一个调用指令轻松找到它。
import struct
def xor(a, b):
return bytearray([a[0]^b[0], a[1]^b[1], a[2]^b[2], a[3]^b[3]])
with open("aaa9", "rb") as f:
data = f.read()
ba = 0x4c
key = data[ba:ba+4]
print("Key : {}".format(key))
size = struct.unpack("I", xor(key, data[ba+4:ba+8]))[0]
print("Size : {}".format(size))
res = bytearray()
i = ba+8
while i < (len(data) - ba - 8):
d = data[i:i+4]
res += xor(d, key)
key = d
i += 4
with open("a.out", "wb+") as f:
f.write(res)
我们得到一个PE文件:3c9a06b2477694919b1c77d3288984cb793a47dd328ef39e15132cd0cfb593ab。

令人惊讶的是直接在此看到PE文件,因为它是由上一次调用直接执行的。诀窍在于,实际上将PE文件修改为一个有效的PE文件,并且可以直接直接执行(Cobalt Strike称为无级有效负载)。我们在这里看到正在执行的MZ标头:

修改了DOS标头,使其包含跳至二进制文件中地址0x8157的有效指令。此地址是导出_ReflectiveLoader@4
函数的地址,该函数基于RefelctiveDLLInection软件,负责在调用入口点之前再现一个简单的PE加载器以加载和映射导入函数。
(请注意,这仅在Cobalt Strike中是可选的,许多Cobalt Strike有效负载不具有PE文件格式,而是直接类似于Shellcode格式的有效负载)。
提取配置
Cobalt Strike配置在有效负载中进行了加密,并根据Cobalt Strike版本使用不同的密钥(0x23或0x69)。解码后,配置以type-length-value格式存储:
- 一个简短的表示数据密钥的列表(可以在Cobalt Strike源代码中找到该列表)
- 两个短字节代表数据类型(Int,Short,String等)及其长度
- 数据本身
要找到配置的起始地址,我们可以查找第一个密钥的编码值,即1(DNS / SSL),1(Short),2(2个字节),并ihihik
使用密钥0x69或././.,
使用键0x23。
然后,我们可以解码信标的配置:
dns False
ssl True
port 443
.sleeptime 60000
.http-get.server.output 00000004000000010000017700000001000000fa0000000200000004000000020000001c000000020000002400000002000000120000000200000004000000020000001c0000000200000024000000020000001100000002000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.jitter 15
.maxdns 255
publickey 30819f300d06092a864886f70d010101050003818d0030818902818100aef69a6fb8f21092c01a95cbdcac0f03f79738adecda36cffc6c5cf607943e72663865f8f69d84961910201ffde089b24cd4352c766414d0665537956b8ec8f4e23df0cd79e9284c16c899fde818758a22c53947e3dd52f440be86f71cdf8abb79adb3b8afaf9f80af028d823f1d70fcdbb34b0b5f5293f74dbb184a3c9109f3020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.http-get.uri 156.226.191.234,/_/scs/mail-static/_/js/,djiqowenlsakdj.com,/_/scs/mail-static/_/js/
.user-agent Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MALCJS)
.http-post.uri /mail/u/0/
.http-get.client OSID=Cookie
GAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
ui=d3244c4707ient
hop=6928632 start=0
=Content-Type: application/x-www-form-urlencoded;charset=utf-8OSID=Cookie
.spawto
.post-ex.spawnto_x86 %windir%\syswow64\notepad.exe
.post-ex.spawnto_x64 %windir%\sysnative\notepad.exe
.pipename
.cryptoscheme 0
.dns_idle 134743044
.dns_sleep 0
.http-get.verb GET
.http-post.verb POST
shouldChunkPosts 0
.watermark 305419896
.stage.cleanup 0
CFGCaution 0
host_header
cookieBeacon 1
.proxy_type 2
funk 0
killdate 0
text_section 0
process-inject-start-rwx 64
process-inject-use-rwx 64
process-inject-min_alloc 0
process-inject-transform-x86
process-inject-transform-x64
process-inject-stub a56c813864af878a4c10083ca1578e0a
process-inject-execute
process-inject-allocation-method 0
合并结果
既然我们已经解码了所有内容,那么很容易执行请求,直接提取信标和配置。我把所有这些都放在这个github仓库中的脚本中:
$ python scan.py https://103.39.18.184/
Checking https://103.39.18.184/
Unknown config command 55
Configuration of the x86 payload
dns False
ssl True
port 443
.sleeptime 60000
.http-get.server.output 00000004000000010000017700000001000000fa0000000200000004000000020000001c000000020000002400000002000000120000000200000004000000020000001c0000000200000024000000020000001100000002000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.jitter 15
.maxdns 255
[SNIP]
x86_64: Payload not found
通常会为许多不同的样本找到相同的配置,因为CS使用了他们所谓的可锻C2配置文件,这些配置文件实际上是CS信标的配置,可以通过配置文件轻松共享。例如,Ocean Lotus使用模仿Google Safebrowsing url的公共配置文件。该存储库列出了不同的APT或网络犯罪组使用的配置文件。
配置中一个有趣的值是水印,它是从许可证文件生成的数字。由于它是客户特有的,因此可用于将多个CobaltStrike实例枢轴连接和链接在一起(就像Trickbot所做的那样。因此,许多CobaltStrike的破解版本都禁用了此水印。该水印在技术上与Cobalt Strike客户相关联id,因此应该可以将此ID报告给Cobalt Strike并为使用付费许可证的人员标识客户,但是我从未听说过有人这样做(我猜很少有APT组拥有有效的CS许可证)。
提取1000个Cobalt Strike服务器的配置
基于相同的代码,我扫描了Shodan中用JARM标识的3424台服务器,并使用此脚本扫描了所有服务器,并发现了520个使用Cobalt Strike信标的服务器。
我在GitHub上传了一个csv,其中列出了这些信标的IP和配置,这是一个简短的摘录:
host | 获取URI | POST URI | 用户代理 | 水印 |
---|---|---|---|---|
54.66.253.144 | 54.66.253.144,/s/ref=nb_sb_noss_1/167-3294888-0262949/field-keywords=books | /N4215/adj/amzn.us.sr.aps | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko | 562884990 |
103.243.183.250 | 103.243.183.250,/search.js | /hr | Mozilla/5.0 (Linux; Android 6.0; HTC One X10 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 | 305419896 |
185.82.126.47 | 185.82.126.47,/pixel | /submit.php | Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MASB) | 305419896 |
94.156.174.121 | 94.156.174.121,/watch | /ptracking | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) | 76803050 |
194.36.191.118 | 194.36.191.118,/visit.js | /submit.php | Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; InfoPath.2; .NET CLR 2.0.50727) | 305419896 |
23.106.160.198 | repshd.com,/us/ky/louisville/312-s-fourth-st.html,pinglis.com,/us/ky/louisville/312-s-fourth-st.html,stargut.com,/us/ky/louisville/312-s-fourth-st.html | /OrderEntryService.asmx/AddOrderLine | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) | 0 |
23.81.246.46 | contmetric.com,/s/ref=nb_sb_noss_1/167-3294888-0262949/field-keywords=books | /N4215/adj/amzn.us.sr.aps | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko | 0 |
108.174.193.11 | qw.removerchangefile.monster,/media.html,as.removerchangefile.monster,/media.html,zx.removerchangefile.monster,/media.html | /ak | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9 | 305419896 |
213.217.0.218 | 213.217.0.218,/visit.js | /submit.php | Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; NP06) | 305419896 |
213.252.247.31 | 1nubjgrcfjhjhkjftdd.com,/s/ref=nb_sb_noss_1/167-3294888-0262949/field-keywords=books | /N4215/adj/amzn.us.sr.aps | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko | 0 |
其中有523个中的165个没有水印,160个IP使用了另一个水印(305419896),因此它可能是默认值。然后,一些水印具有5个以上的服务器,例如1580103814、1873343027或16777216。
520个使用Cobalt Strike信标服务器列表下载地址
雨苁网盘: https://w.ddosi.workers.dev/

我已经在Github上上传了所有这些信标脚本和yara规则,可以在Twitter上直接向我发送DM或向我发送电子邮件,如果您有任何疑问。