前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分析Cobalt Strike Payload

分析Cobalt Strike Payload

作者头像
黑白天安全
发布2021-09-28 15:51:02
1.5K0
发布2021-09-28 15:51:02
举报

原始Payload

Cobalt Strike 的Payload基于 Meterpreter shellcode,例如 API 哈希(x86和x64版本)或http/https Payload中使用的url checksum8 算法等等。

x86

默认的 32 位原始负载的入口点以典型指令开始,CLD (0xFC),然后是CALL指令,并PUSHA (0x60)作为 API 哈希算法的第一条指令。

x64

默认 64 位原始负载也以CLD指令开头,然后是AND RSP,-10h和CALL指令。

我们可以使用这些来定位Payload的入口点,并从该位置计算其他固定偏移量。

默认 API 哈希

原始负载具有预定义的结构和二进制格式,每个可自定义值(例如 DNS 查询、HTTP 标头或 C2 IP 地址)都有特定的占位符。占位符偏移位于与硬编码 API 哈希值相同的固定位置。散列算法是ROR13,最终散列是根据 API 函数名称和 DLL 名称计算得出的。整个算法在 Metasploit 存储库的汇编代码中得到了很好的注释。

我们可以使用以下正则表达式来搜索硬编码的 API 哈希:

我们可以使用已知的 API 哈希列表来正确识别负载类型,并使用 API 哈希的已知固定位置,以便通过 Yara 规则进行更准确的检测。

完整的 Cobalt Strike API 哈希列表:

代码语言:javascript
复制
API 哈希      DLL 和 API 名称
0xc99cc96a dnsapi.dll_DnsQuery_A
0x528796c6 kernel32.dll_CloseHandle
0xe27d6f28 kernel32.dll_ConnectNamedPipe
0xd4df7045 kernel32.dll_CreateNamedPipeA
0xfcddfac0 kernel32.dll_DisconnectNamedPipe
0x56a2b5f0 kernel32.dll_ExitProcess
0x5de2c5aa kernel32.dll_GetLastError
0x0726774c kernel32.dll_LoadLibraryA
0xcc8e00f4 kernel32.dll_lstrlenA
0xe035f044 kernel32.dll_Sleep
0xbb5f9ead kernel32.dll_ReadFile
0xe553a458 kernel32.dll_VirtualAlloc
0x315e2145 user32.dll_GetDesktopWindow
0x3b2e55eb wininet.dll_HttpOpenRequestA
0x7b18062d wininet.dll_HttpSendRequestA
0xc69f8957 wininet.dll_InternetConnectA
0x0be057b7 wininet.dll_InternetErrorDlg
0xa779563a wininet.dll_InternetOpenA
0xe2899612 wininet.dll_InternetReadFile
0x869e4675 wininet.dll_InternetSetOptionA
0xe13bec74 ws2_32.dll_accept
0x6737dbc2 ws2_32.dll_bind
0x614d6e75 ws2_32.dll_closesocket
0x6174a599 ws2_32.dll_connect
0xff38e9b7 ws2_32.dll_listen
0x5fc8d902 ws2_32.dll_recv
0xe0df0fea ws2_32.dll_WSASocketA
0x006b8029 ws2_32.dll_WSAStartup

客户 ID/水印

根据官方网页上提供的信息,客户 ID 是一个与 Cobalt Strike 许可证密钥相关联的 4 字节数字,因为 v3.9 已嵌入到负载和信标配置中。如果存在,则此编号位于负载的末尾。

客户 ID 可用于特定威胁作者的识别或归属,但很多客户 ID 来自破解或泄露的版本,因此请在查看这些可能的归属时考虑这一点。

DNS stager x86

典型的payload大小为 515 字节或 519 字节,包括客户 ID 值。

DNS 查询名称字符串从偏移量 0x0140(从负载入口点计算)开始,空字节和最大字符串大小为 63 字节。如果 DNS 查询名称字符串较短,则以空字节终止,字符串空间的其余部分填充为垃圾字节。

DnsQuery_A 调用 API 函数时使用两个默认参数:

DNS 记录类型(wType) 0x0010 DNS_TYPE_TEXT DNS 查询选项(选项) 0x0248 DNS_QUERY_BYPASS_CACHE DNS_QUERY_NO_HOSTS_FILE DNS_QUERY_RETURN_MESSAGE

默认值以外的任何内容都是可疑的,可能为自定义payload。

Python解析:

默认 DNS 负载 API 哈希值:

代码语言:javascript
复制
0x00a3 0xe553a458 kernel32.dll_VirtualAlloc
0x00bd 0x0726774c kernel32.dll_LoadLibraryA
0x012f 0xc99cc96a dnsapi.dll_DnsQuery_A
0x0198 0x56a2b5f0 kernel32.dll_ExitProcess
0x01a4 0xe035f044 kernel32.dll_Sleep
0x01e4 0xcc8e00f4 kernel32.dll_lstrlenA

DNS stager 的 Yara 规则:

SMB stager x86

默认负载大小为 346 字节加上以空字节结尾的管道名称字符串的长度和客户 ID 的长度(如果存在)。管道名称字符串以明文格式位于偏移量 0x015A 上的负载代码之后。

CreateNamedPipeA API 函数使用 3 个默认参数调用:

代码语言:javascript
复制
Open Mode (dwOpenMode) 0x0003 PIPE_ACCESS_DUPLEX
Pipe Mode (dwPipeMode) 0x0006 PIPE_TYPE_MESSAGE, PIPE_READMODE_MESSAGE
Max Instances (nMaxInstances) 0x0001

Python解析:

默认 SMB 负载 API 哈希值:

代码语言:javascript
复制
0x00a1 0xe553a458 kernel32.dll_VirtualAlloc
0x00c4 0xd4df7045 kernel32.dll_CreateNamedPipeA
0x00d2 0xe27d6f28 kernel32.dll_ConnectNamedPipe
0x00f8 0xbb5f9ead kernel32.dll_ReadFile
0x010d 0xbb5f9ead kernel32.dll_ReadFile
0x0131 0xfcddfac0 kernel32.dll_DisconnectNamedPipe
0x0139 0x528796c6 kernel32.dll_CloseHandle
0x014b 0x56a2b5f0 kernel32.dll_ExitProcess

SMB stager 的 Yara 规则:

TCP Bind stager x86

负载大小为 332 字节加上客户 ID 的长度(如果存在)。

绑定 API 函数的参数存储在SOCKADDR_IN硬编码为两个双字推送的结构中。PUSH具有 sin_addr 值的第一个位于偏移量 0x00C4 上。第二个PUSH包含 sin_port 和 sin_family 值,位于偏移量 0x00C9 上。

默认 sin_family 值为AF_INET(0x02)

Python解析:

TCP Bind stager x86负载 API 哈希值:

代码语言:javascript
复制
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00ac 0x006b8029 ws2_32.dll_WSAStartup
0x00bb 0xe0df0fea ws2_32.dll_WSASocketA
0x00d5 0x6737dbc2 ws2_32.dll_bind
0x00de 0xff38e9b7 ws2_32.dll_listen
0x00e8 0xe13bec74 ws2_32.dll_accept
0x00f1 0x614d6e75 ws2_32.dll_closesocket
0x00fa 0x56a2b5f0 kernel32.dll_ExitProcess
0x0107 0x5fc8d902 ws2_32.dll_recv
0x011a 0xe553a458 kernel32.dll_VirtualAlloc
0x0128 0x5fc8d902 ws2_32.dll_recv
0x013d 0x614d6e75 ws2_32.dll_closesocket

TCP Bind stager x86的 Yara 规则:

TCP Bind stager x64

负载大小为 510 字节加上客户 ID 的长度(如果存在)。该SOCKADDR_IN结构在MOV指令内硬编码为 qword 并包含整个结构。该MOV指令的偏移量为0x00EC。

Python解析:

TCP Bind stager x64负载 API 哈希值:

代码语言:javascript
复制
0x0100 0x0726774c kernel32.dll_LoadLibraryA
0x0111 0x006b8029 ws2_32.dll_WSAStartup
0x012d 0xe0df0fea ws2_32.dll_WSASocketA
0x0142 0x6737dbc2 ws2_32.dll_bind
0x0150 0xff38e9b7 ws2_32.dll_listen
0x0161 0xe13bec74 ws2_32.dll_accept
0x016f 0x614d6e75 ws2_32.dll_closesocket
0x0198 0x5fc8d902 ws2_32.dll_recv
0x01b8 0xe553a458 kernel32.dll_VirtualAlloc
0x01d2 0x5fc8d902 ws2_32.dll_recv
0x01ee 0x614d6e75 ws2_32.dll_closesocket

TCP Bind stager x64的 Yara 规则:

TCP Reverse stager x86

负载大小为 290 字节加上客户 ID 的长度(如果存在)。此Payload与 TCP Bind stager x64 非常相似,并且SOCKADDR_IN结构使用相同的双推送指令硬编码在相同的偏移量上,因此我们可以重用来自 TCP Bind stager x64 Payload的 Python 解析代码。

默认TCP Reverse stager x86负载 API 哈希值:

代码语言:javascript
复制
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00ac 0x006b8029 ws2_32.dll_WSAStartup
0x00bb 0xe0df0fea ws2_32.dll_WSASocketA
0x00d5 0x6174a599 ws2_32.dll_connect
0x00e5 0x56a2b5f0 kernel32.dll_ExitProcess
0x00f2 0x5fc8d902 ws2_32.dll_recv 0x0105
0xe553a458 kernel32.dll_VirtualAlloc
0x0113 0x5fc8d902 ws2_32.dll_recv

TCP Reverse x86 stager 的 Yara 规则:

TCP Reverse x64 stager

默认负载大小为 465 字节加上客户 ID 的长度(如果存在)。Payload SOCKADDR_IN与 TCP Bind x64 payload 等结构具有相同的位置,因此我们可以再次重用解析代码。

TCP Reverse x64 stager 负载 API 哈希值

代码语言:javascript
复制
0x0100 0x0726774c kernel32.dll_LoadLibraryA
0x0111 0x006b8029 ws2_32.dll_WSAStartup
0x012d 0xe0df0fea ws2_32.dll_WSASocketA
0x0142 0x6174a599 ws2_32.dll_connect
0x016b 0x5fc8d902 ws2_32.dll_recv
0x018b 0xe553a458 kernel32.dll_VirtualAlloc
0x01a5 0x5fc8d902 ws2_32.dll_recv
0x01c1 0x614d6e75 ws2_32.dll_closesocket

TCP Reverse x64 stager 的 Yara 规则:

HTTP stager x86 和 x64

默认 x86 负载大小为 780 字节,x64 版本为 874 字节长加上请求地址字符串的大小和客户 ID 的大小(如果存在)。负载包括存储在多个占位符中的完整请求信息。

请求地址

请求地址是一个由空字节终止的纯文本字符串,位于最后一个Payload指令之后,没有任何填充。x86 版本的偏移量是 0x030C,x64 负载版本的偏移量是 0x036A。典型的格式是 IPv4.

请求端口

对于 x86 版本,请求端口值在PUSH指令中硬编码为双字。PUSH指令的偏移量为0x00BE。x64 版本的端口值存储MOV r8d, dword在偏移量 0x010D 上的指令中。

请求查询

请求查询的占位符的最大大小为 80 个字节,该值是一个以空字节结尾的纯文本字符串。如果请求查询字符串较短,则字符串空间的其余部分填充为垃圾字节。x86 版本的占位符偏移量是 0x0143,x64 版本的占位符偏移量是 0x0186。

Cobalt Strike 和其他工具(例如 Metasploit)使用简单的 checksum8 算法进行请求查询,以区分 x86 和 x64 负载或信标。

根据泄露的 Java Web 服务器源代码,Cobalt Strike 仅使用两个校验和值,0x5C (92) 用于 x86 负载,0x5D 用于 x64 版本。还有一些 Strict stager 变体的实现,其中请求查询字符串的长度必须为 5 个字符(包括斜杠)。请求查询校验和功能不是强制性的。

checksum8 算法的 Python 实现:

Metasploit 服务器使用类似的值:

https://github.com/avast/ioc/tree/master/CobaltStrike/checksum8

请求头

请求头占位符的大小为 304 字节,该值也表示为以空字节结尾的纯文本字符串。请求标头占位符位于请求查询占位符之后。x86 版本的偏移量为 0x0193,x64 版本的偏移量为 0x01D6。

HTTP/HTTPS stager 的典型请求标头值是 User-Agent。Cobalt Strike Web 服务器已禁止以 lynx、curl 或 wget 开头的用户代理,如果找到这些字符串中的任何一个,则返回响应代码 404。

API 函数HttpOpenRequestA使用以下 dwFlags ( 0x84600200)调用:

Python解析:

默认 HTTP x86 负载 API 哈希值:

代码语言:javascript
复制
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00aa 0xa779563a wininet.dll_InternetOpenA
0x00c6 0xc69f8957 wininet.dll_InternetConnectA
0x00de 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x00f2 0x7b18062d wininet.dll_HttpSendRequestA
0x010b 0x5de2c5aa kernel32.dll_GetLastError
0x0114 0x315e2145 user32.dll_GetDesktopWindow
0x0123 0x0be057b7 wininet.dll_InternetErrorDlg
0x02c4 0x56a2b5f0 kernel32.dll_ExitProcess
0x02d8 0xe553a458 kernel32.dll_VirtualAlloc
0x02f3 0xe2899612 wininet.dll_InternetReadFile

默认 HTTP x64 负载 API 哈希值:

代码语言:javascript
复制
0x00e9 0x0726774c kernel32.dll_LoadLibraryA
0x0101 0xa779563a wininet.dll_InternetOpenA
0x0120 0xc69f8957 wininet.dll_InternetConnectA
0x013f 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x0163 0x7b18062d wininet.dll_HttpSendRequestA
0x0308 0x56a2b5f0 kernel32.dll_ExitProcess
0x0324 0xe553a458 kernel32.dll_VirtualAlloc
0x0342 0xe2899612 wininet.dll_InternetReadFile

HTTP x86 和 x64 stager 的 Yara 规则:

HTTPS stager x86 和 x64

负载结构和占位符几乎与 HTTP stager 相同。

区别仅在于Payload大小、占位符偏移、InternetSetOptionAAPI 函数的使用(API 哈希 0x869e4675)和调用HttpOpenRequestAAPI 函数的不同 dwFlags 。

默认的 x86 负载大小为 817 字节,x64 版本的默认值为 909 字节长加上请求地址字符串的大小和客户 ID 的大小(如果存在)。

请求地址

x86 版本的占位符偏移量是 0x0331 和 x64 负载版本的 0x038D。典型的格式是 IPv4。

请求端口

硬编码的请求端口格式与 HTTP 相同。PUSHx86 版本的偏移量为 0x00C3。MOVx64 版本的指令位于偏移量 0x0110 上。

请求查询

请求查询的占位符具有与 HTTP 版本相同的格式和长度。x86 版本的占位符偏移量是 0x0168,x64 版本的占位符偏移量是 0x01A9。

请求头

请求头占位符的大小和长度与 HTTP 版本相同。x86 版本的偏移量是 0x01B8,x64 版本的偏移量是 0x01F9。

API 函数HttpOpenRequestA使用以下 dwFlags ( 0x84A03200)调用:

InternetSetOptionA API 函数使用以下参数调用:

Python解析:

默认 HTTPS x86 负载 API 哈希值:

代码语言:javascript
复制
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00af 0xa779563a wininet.dll_InternetOpenA
0x00cb 0xc69f8957 wininet.dll_InternetConnectA
0x00e7 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x0100 0x869e4675 wininet.dll_InternetSetOptionA
0x0110 0x7b18062d wininet.dll_HttpSendRequestA
0x0129 0x5de2c5aa kernel32.dll_GetLastError
0x0132 0x315e2145 user32.dll_GetDesktopWindow
0x0141 0x0be057b7 wininet.dll_InternetErrorDlg
0x02e9 0x56a2b5f0 kernel32.dll_ExitProcess
0x02fd 0xe553a458 kernel32.dll_VirtualAlloc
0x0318 0xe2899612 wininet.dll_InternetReadFile

默认 HTTPS x64 负载 API 哈希值:

代码语言:javascript
复制
0x00e9 0x0726774c kernel32.dll_LoadLibraryA
0x0101 0xa779563a wininet.dll_InternetOpenA
0x0123 0xc69f8957 wininet.dll_InternetConnectA
0x0142 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x016c 0x869e4675 wininet.dll_InternetSetOptionA
0x0186 0x7b18062d wininet.dll_HttpSendRequestA
0x032b 0x56a2b5f0 kernel32.dll_ExitProcess
0x0347 0xe553a458 kernel32.dll_VirtualAlloc
0x0365 0xe2899612 wininet.dll_InternetReadFile

HTTPS x86 和 x64 stager 的 Yara 规则:

可以通过 curl 或 wget 工具直接下载下一阶段或信标:

Raw Payloads

Cobalt Strike 还包括一个Payload生成器,用于以多种编码格式导出原始 stager 和Payload。编码格式支持 UTF-8 和 UTF-16le。

最常见的编码表及其用法和示例:

代码语言:javascript
复制
Hex                                       VBS, HTA                         4d5a9000..
Hex Array                                 PS1                              0x4d, 0x5a, 0x90, 0x00..
Hex Veil                                  PY                               \x4d\x5a\x90\x00..
Decimal Array                             VBA                               -4,-24,-119,0..
Char Array                                VBS, HTA                          Chr(-4)&”H”&Chr(-125)..
Base64                                    PS1                               38uqIyMjQ6..
gzip / deflate compression                PS1
 Xor                                      PS1, Raw payloads, Beacons

解码大多数格式非常简单,

Decimal 和 Char Array 中的值通过由“\s_\n”(\x20\x5F\x0A)表示的“新行”分割。

PowerShell 脚本中使用的常见压缩算法是 GzipStream 和原始 DeflateStream。

注:公众号之前分析了power shell的可以去看看

Python解压实现:

异或编码

XOR 算法用于三种不同的情况。第一种情况是 PS1 脚本中的一个字节 XOR,默认值为 35 (0x23)。

注:这个我们之前分析过

第二种用法是与 dword 密钥进行异或,用于编码 PE stagers 二进制文件中的原始Payload或信标。异或数据的特定标头长 16 字节,包括起始偏移量、异或数据大小、异或键和四个 0x61 垃圾/填充字节。

Python头解析:

我们可以根据来自标头和编码数据的第一个双字的 XOR 键创建 Yara 规则,以验证那里的假设值:

第三种情况是使用滚动双字密钥进行异或编码,仅用于解码下载的信标。编码的数据 blob 位于 XOR 算法代码之后,没有任何填充。编码数据以初始 XOR 密钥 (dword) 和数据大小(dword 与 init 密钥异或)开始。

XOR 算法有 x86 和 x64 实现。Cobalt Strike 资源包括带有预编译 XOR 算法代码的 xor.bin 和 xor64.bin 文件。

编译后的 x86 代码的默认长度为 52 和 56 字节(取决于使用的寄存器)加上垃圾字节的长度。x86 实现允许使用不同的寄存器集,因此 xor.bin 文件包含 800 多种不同的预编译代码变体。

使用 XOR 验证覆盖所有 x86 变体的 Yara 规则:

预编译的 x64 代码长 63 个字节,没有垃圾字节。也只有一种预编译代码变体。

带有 XOR 验证的 x64 变体的 Yara 规则:

Raw Payload 解码器和提取器和一个 IDAPython 脚本

https://github.com/avast/ioc/tree/master/CobaltStrike

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 黑白天实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档