分析Cobalt Strike Payload
原始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 哈希列表:
API 哈希 DLL 和 API 名稱0xc99cc96a dnsapi.dll_DnsQuery_A0x528796c6 kernel32.dll_CloseHandle0xe27d6f28 kernel32.dll_ConnectNamedPipe0xd4df7045 kernel32.dll_CreateNamedPipeA0xfcddfac0 kernel32.dll_DisconnectNamedPipe0x56a2b5f0 kernel32.dll_ExitProcess0x5de2c5aa kernel32.dll_GetLastError0x0726774c kernel32.dll_LoadLibraryA0xcc8e00f4 kernel32.dll_lstrlenA0xe035f044 kernel32.dll_Sleep0xbb5f9ead kernel32.dll_ReadFile0xe553a458 kernel32.dll_VirtualAlloc0x315e2145 user32.dll_GetDesktopWindow0x3b2e55eb wininet.dll_HttpOpenRequestA0x7b18062d wininet.dll_HttpSendRequestA0xc69f8957 wininet.dll_InternetConnectA0x0be057b7 wininet.dll_InternetErrorDlg0xa779563a wininet.dll_InternetOpenA0xe2899612 wininet.dll_InternetReadFile0x869e4675 wininet.dll_InternetSetOptionA0xe13bec74 ws2_32.dll_accept0x6737dbc2 ws2_32.dll_bind0x614d6e75 ws2_32.dll_closesocket0x6174a599 ws2_32.dll_connect0xff38e9b7 ws2_32.dll_listen0x5fc8d902 ws2_32.dll_recv0xe0df0fea ws2_32.dll_WSASocketA0x006b8029 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 哈希值:
0x00a3 0xe553a458 kernel32.dll_VirtualAlloc0x00bd 0x0726774c kernel32.dll_LoadLibraryA0x012f 0xc99cc96a dnsapi.dll_DnsQuery_A0x0198 0x56a2b5f0 kernel32.dll_ExitProcess0x01a4 0xe035f044 kernel32.dll_Sleep0x01e4 0xcc8e00f4 kernel32.dll_lstrlenA
DNS stager 的 Yara 規則:

SMB stager x86
默認負載大小為 346 字節加上以空字節結尾的管道名稱字符串的長度和客戶 ID 的長度(如果存在)。管道名稱字符串以明文格式位于偏移量 0x015A 上的負載代碼之后。
CreateNamedPipeA API 函數使用 3 個默認參數調用:
Open Mode (dwOpenMode) 0x0003 PIPE_ACCESS_DUPLEXPipe Mode (dwPipeMode) 0x0006 PIPE_TYPE_MESSAGE, PIPE_READMODE_MESSAGEMax Instances (nMaxInstances) 0x0001
Python解析:

默認 SMB 負載 API 哈希值:
0x00a1 0xe553a458 kernel32.dll_VirtualAlloc0x00c4 0xd4df7045 kernel32.dll_CreateNamedPipeA0x00d2 0xe27d6f28 kernel32.dll_ConnectNamedPipe0x00f8 0xbb5f9ead kernel32.dll_ReadFile0x010d 0xbb5f9ead kernel32.dll_ReadFile0x0131 0xfcddfac0 kernel32.dll_DisconnectNamedPipe0x0139 0x528796c6 kernel32.dll_CloseHandle0x014b 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 哈希值:
0x009c 0x0726774c kernel32.dll_LoadLibraryA0x00ac 0x006b8029 ws2_32.dll_WSAStartup0x00bb 0xe0df0fea ws2_32.dll_WSASocketA0x00d5 0x6737dbc2 ws2_32.dll_bind0x00de 0xff38e9b7 ws2_32.dll_listen0x00e8 0xe13bec74 ws2_32.dll_accept0x00f1 0x614d6e75 ws2_32.dll_closesocket0x00fa 0x56a2b5f0 kernel32.dll_ExitProcess0x0107 0x5fc8d902 ws2_32.dll_recv0x011a 0xe553a458 kernel32.dll_VirtualAlloc0x0128 0x5fc8d902 ws2_32.dll_recv0x013d 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 哈希值:
0x0100 0x0726774c kernel32.dll_LoadLibraryA0x0111 0x006b8029 ws2_32.dll_WSAStartup0x012d 0xe0df0fea ws2_32.dll_WSASocketA0x0142 0x6737dbc2 ws2_32.dll_bind0x0150 0xff38e9b7 ws2_32.dll_listen0x0161 0xe13bec74 ws2_32.dll_accept0x016f 0x614d6e75 ws2_32.dll_closesocket0x0198 0x5fc8d902 ws2_32.dll_recv0x01b8 0xe553a458 kernel32.dll_VirtualAlloc0x01d2 0x5fc8d902 ws2_32.dll_recv0x01ee 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 哈希值:
0x009c 0x0726774c kernel32.dll_LoadLibraryA0x00ac 0x006b8029 ws2_32.dll_WSAStartup0x00bb 0xe0df0fea ws2_32.dll_WSASocketA0x00d5 0x6174a599 ws2_32.dll_connect0x00e5 0x56a2b5f0 kernel32.dll_ExitProcess0x00f2 0x5fc8d902 ws2_32.dll_recv 0x01050xe553a458 kernel32.dll_VirtualAlloc0x0113 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 哈希值
0x0100 0x0726774c kernel32.dll_LoadLibraryA0x0111 0x006b8029 ws2_32.dll_WSAStartup0x012d 0xe0df0fea ws2_32.dll_WSASocketA0x0142 0x6174a599 ws2_32.dll_connect0x016b 0x5fc8d902 ws2_32.dll_recv0x018b 0xe553a458 kernel32.dll_VirtualAlloc0x01a5 0x5fc8d902 ws2_32.dll_recv0x01c1 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 哈希值:
0x009c 0x0726774c kernel32.dll_LoadLibraryA0x00aa 0xa779563a wininet.dll_InternetOpenA0x00c6 0xc69f8957 wininet.dll_InternetConnectA0x00de 0x3b2e55eb wininet.dll_HttpOpenRequestA0x00f2 0x7b18062d wininet.dll_HttpSendRequestA0x010b 0x5de2c5aa kernel32.dll_GetLastError0x0114 0x315e2145 user32.dll_GetDesktopWindow0x0123 0x0be057b7 wininet.dll_InternetErrorDlg0x02c4 0x56a2b5f0 kernel32.dll_ExitProcess0x02d8 0xe553a458 kernel32.dll_VirtualAlloc0x02f3 0xe2899612 wininet.dll_InternetReadFile
默認 HTTP x64 負載 API 哈希值:
0x00e9 0x0726774c kernel32.dll_LoadLibraryA0x0101 0xa779563a wininet.dll_InternetOpenA0x0120 0xc69f8957 wininet.dll_InternetConnectA0x013f 0x3b2e55eb wininet.dll_HttpOpenRequestA0x0163 0x7b18062d wininet.dll_HttpSendRequestA0x0308 0x56a2b5f0 kernel32.dll_ExitProcess0x0324 0xe553a458 kernel32.dll_VirtualAlloc0x0342 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 哈希值:
0x009c 0x0726774c kernel32.dll_LoadLibraryA0x00af 0xa779563a wininet.dll_InternetOpenA0x00cb 0xc69f8957 wininet.dll_InternetConnectA0x00e7 0x3b2e55eb wininet.dll_HttpOpenRequestA0x0100 0x869e4675 wininet.dll_InternetSetOptionA0x0110 0x7b18062d wininet.dll_HttpSendRequestA0x0129 0x5de2c5aa kernel32.dll_GetLastError0x0132 0x315e2145 user32.dll_GetDesktopWindow0x0141 0x0be057b7 wininet.dll_InternetErrorDlg0x02e9 0x56a2b5f0 kernel32.dll_ExitProcess0x02fd 0xe553a458 kernel32.dll_VirtualAlloc0x0318 0xe2899612 wininet.dll_InternetReadFile
默認 HTTPS x64 負載 API 哈希值:
0x00e9 0x0726774c kernel32.dll_LoadLibraryA0x0101 0xa779563a wininet.dll_InternetOpenA0x0123 0xc69f8957 wininet.dll_InternetConnectA0x0142 0x3b2e55eb wininet.dll_HttpOpenRequestA0x016c 0x869e4675 wininet.dll_InternetSetOptionA0x0186 0x7b18062d wininet.dll_HttpSendRequestA0x032b 0x56a2b5f0 kernel32.dll_ExitProcess0x0347 0xe553a458 kernel32.dll_VirtualAlloc0x0365 0xe2899612 wininet.dll_InternetReadFile
HTTPS x86 和 x64 stager 的 Yara 規則:

可以通過 curl 或 wget 工具直接下載下一階段或信標:

Raw Payloads
Cobalt Strike 還包括一個Payload生成器,用于以多種編碼格式導出原始 stager 和Payload。編碼格式支持 UTF-8 和 UTF-16le。
最常見的編碼表及其用法和示例:
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_”(\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/CobaltStrik