This document discusses using vectored exception handlers and hardware breakpoints to bypass security products like EDR tools. It describes how attackers can register vectored exception handlers to programmatically handle exceptions raised by hardware breakpoints. This allows attackers to intercept function calls and manipulate execution flow without directly patching memory. The document analyzes real malware samples that use these techniques, and explores ways security researchers can trace the use of hardware breakpoints through ETW logging of the NtSetContextThread system call.
WSO2Con2024 - Unleashing the Financial Potential of 13 Million People
Unmasking the Dark Art of Vectored Exception Handling: Bypassing XDR and EDR in the Evolving Cyber Threat Landscape
1. #BHMEA23 www.blackhatmea.com
|
|
Unmasking the Dark Art of Vectored Exception
Handling: Bypassing XDR and EDR in the
Evolving Cyber Threat Landscape
Donato Onofri - Sr. Red Team Engineer, CrowdStrike
Sarang Sonawane - Threat Research Engineer III, CrowdStrike
14 - 16 NOVEMBER 2023
RIYADH, SAUDI ARABIA
ORGANISED BY: IN ASSOCIATION WITH:
2. #BHMEA23 www.blackhatmea.com
|
|
WHOAMI
● Donato Onofri
○ Sr. Red Team Engineer @CrowdStrike
○ 12+ years of experience in Cyber Industry
○ Red Team, Purple Team, Evasion, Reverse Engineering, OS Internals
○ Co-Author of “Attacking and Exploiting Modern Web Applications”
● Sarang Sonawane
○ Threat Research Engineer @CrowdStrike
○ 7+ years of experience
○ Malware Reversing , Sandbox
3. #BHMEA23 www.blackhatmea.com
|
|
Agenda
● Introduction: The Rise of Patchless Attacks (bypassing EDR)
● Exception Abuse: Hardware Breakpoint & Vectored Exception Handler
● Guloader: A real case of VEH abusing
● Tracing HWBP Installation
● VEH² - Setting hardware breakpoint – the silent way
● Catching the VEH
5. #BHMEA23 www.blackhatmea.com
|
|
INTRODUCTION - THE RISE of PATCHLESS ATTACKS
● To bypass security products, a Red Teamer (and also User Space Malware) adopts several ways to deactivate/bypass Telemetry (like User
Mode Hooks and AMSI). For example, patching in memory the hooked functions, structs or scanning functions (e.g. AmsiScanBuffer).
● The “Memory Patching” approach is noisy (from an attacker perspective) because it can raise several alerts during Memory Integrity
Checks and during the modification itself (VirtualProtect / NtProtectVirtualMemory).
● Red Team Operators have started to avoid “active memory patching” by manipulating execution flow when specific functions are called
by a process: User Space Hooking.
● “Patchless bypass” is one of the most prolific evasion techniques for an attacker, because it allows him to blind XDR telemetry with a
minimal footprint, making it very hard to detect.
6. #BHMEA23 www.blackhatmea.com
|
|
Hooking - Patchless AMSI Bypass Example
● To initiate an AMSI scan, the process calls AmsiScanBuffer, which is an export from amsi.dll. This function forwards the
buffer to be scanned to the registered AMSI provider.
● If the adversaries manages to intercept the AmsiScanBuffer call through hooking, it can manipulate the execution flow,
thereby bypassing the transmission of the buffer to the security product, resulting in no AMSI scan.
AmsiScanBuffer
(amsi dll)
AMSI Scan AMSI Provider
AMSI_RESULT_CLEAN
X
Attacker Process
Hook AmsiScanBuffer
7. #BHMEA23 www.blackhatmea.com
|
|
As attackers have full control over their managed process, they can “install” hooks to manipulate execution flow without the need for
active memory patching .
There are various methods for implementing hooks:
○ NtSetInformationProcess - ProcessInstrumentationCallback
○ Leveraging “Hardware Breakpoints” is one of the most prevalent technique
■Used by: Sharpblock, TamperingSycalls, BlindSide (Cymulate), Nighthawk, BRC4, FireWalker, HAVOC, ..
○ And others ...
Similar to debuggers, attackers can insert breakpoints at specific addresses. This allows them to “interrupt” the execution when a
controlled process is about to execute instructions at the selected addresses, enabling them to manipulate registers (data and
instructions).
Call amsi!AmsiScanBuffer Hardware Breakpoint
triggered: execution stops
Registry manipulation: alter
execution flow and data
Resume execution with new settings
(AmsiScanBuffer never called)
Attacker Places Hardware
Breakpoint on
amsi!AmsiScanBuffer
The challenge for attackers is to find a programmatic way to:
○ set hardware breakpoints (HWBP)
○ control and manage hardware breakpoints (HWBP)
NB: Software breakpoints for hooking are discouraged for use by attackers, as they require memory patching (replacing function
instructions with INT3).
Hardware Breakpoint: how attackers hooks functions
9. #BHMEA23 www.blackhatmea.com
|
|
HARDWARE BREAKPOINT
According to the Intel SDM:
○ DR0-DR3 Debug Registers contain the addresses of
Hardware Breakpoints (can be set at most 4 HW BP).
○ Accessing that location (in combination with the other DR
register), the CPU will raise a Debug Exception.
○ NB: these registers can only be modified in protected-mode
at CPL 0 or in real-address mode and cannot be directly
accessed from user space.
10. #BHMEA23 www.blackhatmea.com
|
|
WINDOWS EXCEPTION HANDLING
Exception:
“In computing and computer programming, exception handling is the process of responding to the occurrence of
exceptions – anomalous or exceptional conditions requiring special processing – during the execution of a program.
In general, an exception breaks the normal flow of execution and executes a pre-registered exception handle” -
source: Wikipedia
Two Main Types of Handler:
● Structured Exception Handler (SEH)
○ try-except-statement :
■__try compound-statement __except (
filter-expression ) compound-statement
● Vectored Exception Handler (VEH)
○PVOID AddVectoredExceptionHandler( ULONG
First, PVECTORED_EXCEPTION_HANDLER
Handler );
11. #BHMEA23 www.blackhatmea.com
|
|
VECTORED EXCEPTION HANDLER - LIST
AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)VEH1); // VEH1 will be called as second
[..]
AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)VEH2); // VEH2 will be called first
[..]
AddVectoredExceptionHandler(0, (PVECTORED_EXCEPTION_HANDLER)VEH3); // VEH3 will be called as third
[..]
AddVectoredExceptionHandler(0, (PVECTORED_EXCEPTION_HANDLER)VEH4); // VEH4 will be call as last
[..]
// EXCEPTION HERE! VEH Order: VEH2 -> VEH1 -> VEH3 -> VEH4
Vectored Exception Handler (VEH)
● When a VEH is registered with AddVectoredExceptionHandler, it is added to the LdrpVectorHandlerList
LinkedList (stored in NTDLL)
● Each Entry in the list contains a reference to a VEH
● During the exception handling, the LdrpVectorHandlerList is traversed like a LIFO (Last-In-First-Out) if
the VEH are registered with First parameter !=0
○if First = 0 the registered VEH will be inserted at the end and called as last
○Example:
12. #BHMEA23 www.blackhatmea.com
|
|
USER EXCEPTION FLOW - VEH
To manage Exception, the Process executes the
KiUserExceptionDispatcher Function (by NTDLL)
● RtlDispatchException
● Passes the Exception Record to the VEH
handlers: RtlCallVectoredHandlers (x2)
● If a VEH inside LdrpVectorHandlerList is
able to manage the Exception:
EXCEPTION_CONTINUE_EXECUTION
● If OK, RtlRestoreContext
● Cleans up the Thread that raised the
Exception
● NtContinue(Context Record)
If within RtlDispatchException, the registered VEHs
cannot handle the exception, the process employs
SEH (Structured Exception Handler), which uses a
frame-base mechanisms to unwind the stack.
In terms of execution priority, VEH always has priority
over SEH.
Additional reading:
○ https://dimitrifourny.github.io/2020/06/11/dumping-
veh-win10.html
13. #BHMEA23 www.blackhatmea.com
|
|
Handling Debug Exception: VECTORED EXCEPTION HANDLER
As observed, it is possible to programmatically handle a specific exception by registering a Vectored Exception Handler
(VEH) using the AddVectoredExceptionHandler API.
The Second parameter: Handler (VEH), a pointer to the handler to be called:
○ The PVECTORED_EXCEPTION_HANDLER type defines a pointer to this callback function: the code that will be executed
when an exception occurs.
Exception occurs, ntdll!RtlCallVectoredHandlers:
○ VEH is called with the argument ExceptionInfo:
■A pointer to EXCEPTION_POINTERS struct that contains the exception info.
VEH
14. #BHMEA23 www.blackhatmea.com
|
|
Handling Debug Exception: VECTORED EXCEPTION HANDLER
When an Exception occurs, the kernel will populate an EXCEPTION_POINTERS struct containing two pointers that will be passed to the VEH:
1. EXCEPTION_RECORD: which contains the ExceptionCode (in case of Debug Exception: to EXCEPTION_SINGLE_STEP 0x80000004 for HW BP), and
ExceptionAddress (where the exception was raised)
2. CONTEXT_RECORD: of the thread that raised the exception, so the handler can edit that struct to manipulate execution and data
VEH
EXCEPTION_RECORD
EXCEPTION_POINTERS
CONTEXT_RECORD
16. #BHMEA23 www.blackhatmea.com
|
|
Exceptions for ANTI-DEBUG and ANTI-ANALYSIS
During the Exception Handling, we noticed that VEH can
manipulate the CONTEXT record, which will call
RtlRestoreContext (so NtContinue), which sets it for the Thread.
This behavior has been extensively abused by malware, not just
for evasion, but also as an Anti-Analysis/Debugging technique:
● When a process is being debugged and a
Breakpoint Exception is raised the debugger would
handle first
● Some Debuggers would not pass the exception to
the program handlers
● Malware can abuse this behavior to hinder the malicious
execution from debuggers:
○ Malware put “malicious” or “execution flow” in the
exception handling, complicating the stepping
○ If the malware is being debugged that code could
not be executed, avoiding to be analyzed
https://0xpat.github.io/Malware_development_part_3/
17. #BHMEA23 www.blackhatmea.com
|
|
GULOADER: VEH ABUSE in ACTION
We observed a more sophisticated approach in Guloader, which
involves:
● Establishing a VEH, where it can manipulate the Execution
Flow by setting the IP register when resuming from that
exception - Source: Malware Analysis: GuLoader Dissection
Reveals New Anti-Analysis Techniques and Code Injection
Redundancy
○ Guloader issue an INT3 instruction to trigger the VEH
(EXCEPTION_BREAKPOINT)
○ the malware performs an XOR operation on the next byte
after EIP and then replaces the EIP on CONTEXT with the
new value, ensuring the execution flow will reach the
correct address.
● As further anti-debug: within the VEH, it also checks the
status of DR register to detect if a Malware Analyst is placing
Hardware Breakpoint for debugging purposes.
19. #BHMEA23 www.blackhatmea.com
|
|
HARDWARE BREAKPOINT & VECTORED EXCEPTION HANDLER for HOOKING
To summarize, for hooking a specific address an attacker needs
to set up:
1. A VEH: to programmatically handle Hardware BreakPoint
Exception (0x80000004: SINGLE_STEP_EXCEPTION)
2. Programmatically place HW BP (defined in Debug
Registers) on an address to hook and to interrupt
execution before execute the address
● will raise an exception (0x80000004:
SINGLE_STEP_EXCEPTION) on it
● NB: Debug registers can be modified at protected-
mode at CPL 0 or in real-address mode (so NOT
directly from user space).
How modifying the Debug Registers in User Space?
SetThreadContext (calls NtSetContextThread syscall
exported by NTDLL):
20. #BHMEA23 www.blackhatmea.com
|
|
SETTHREADCONTEXT
● Patchless AMSI bypass by CCob
○ SharpBlock
● Hardware Breakpoints for Malware v 1.0 by rad9800
○ TamperingSyscalls
○ Unhooking
● BRC4 (IndirectSyscall)
○ AMSI/ETW Bypass (and more)
●HAVOC
○AMSI/ETW Bypass
●BlindSide
○Unhooking
NB: Adopted in the past (>10 yo) by
https://github.com/mmorearty/hardware-breakpoint
All is lost? No more User Space Visibility for Threat Hunters/IR?
Hardware Breakpoints for Malware v 1.0
In-Process Patchless AMSI Bypass
21. #BHMEA23 www.blackhatmea.com
|
|
ALL IS NOT LOST: Tracing NTSETCONTEXTTHREAD
SetThreadContext (WINAPI) calls NtSetContextThread (NTAPI), which
execute a syscall
Take at look at the kernel: NtSetContextThread on ntoskrnl.exe
Before return, the function calls the EtwWrite function, to trace the usage of
NtSetContextThread for this process - regardless if the target thread is
related to a remote or for the same process - logging to the Microsoft-
Windows-Kernel-Audit-API-Calls ETW provider with the
KERNEL_AUDIT_API_SETCONTEXTTHREAD value (Event ID=4)
This will enable us to trace the use NtSetContextThread, even with the
unhooked/indirect syscall user space process!
(NB: tested on Windows 10 21H2 and 22H2, ETW events are very prone to be
edited by Microsoft on updated releases).
23. #BHMEA23 www.blackhatmea.com
|
|
POC: TRACING NTSETCONTEXTTHREAD
PoC: tracing Patchless AMSI bypass by CCob
To confirm HW BreakPoints are set, let’s debug:
Monitoring for Microsoft-Windows-Kernel-Audit-API-Calls ETW Event ID 4 Patchless AMSI Bypass
24. #BHMEA23 www.blackhatmea.com
|
|
NTSETCONTEXTTHREAD – current thread modification
End of the battle? Threat Hunters win?
The NtSetContextThread function is powerful because it allows setting the context not only for the current thread
but also for other threads
However, it is possible that the two examples above modify the context specifically for the current (main) thread,
as they use GetCurrentThread and 0xFFFFFFFE.
We gain insights into why this is is the case by reading the CCob article, which likely relates to the AMSI Bypass:
○ The drawback to hardware breakpoints is that they need to be applied to each thread within the process if you want a process wide bypass.
Setting it on a single thread when loading a .NET DLL from memory works just fine though, since the AMSI scan is performed within the same
thread loading the .NET PE.
NB: When using COM, it typically involves the creation of multiple threads!
26. #BHMEA23 www.blackhatmea.com
|
|
VEH² - CONTEXT MANIPULATION DURING HANDLING
Idea:
○ Just as malware manipulates the RIP register on the CONTEXT to control program execution within the Exception
Handling on a Vectored Exception Handler, it is also feasible to “abuse” the VEH routine to alter the Debug Register in
the CONTEXT, all without the use of any WINAPI or NTAPI functions.
○ Given the objective to edit/modify the CONTEXT of the current thread it is possible to force an exception that can be
handled by another Vectored Exception Handler. This secondary handler, upon resuming execution, can
establish a new CONTEXT with updated Debug Registers using NtContinue.
RtlRestoreContext
(CONTEXT,
_EXCEPTION_RECO
RD)
27. #BHMEA23 www.blackhatmea.com
|
|
VEH² - AMSI BYPASS
Use case: Patchless AMSI Bypass
Register 2 VEH:
○ 1 to handle EXCEPTION_BREAKPOINT (0x8000003): will set new CONTEXT setting the Debug
Register on AmsiScanBuffer address
○ 2 to handle EXCEPTION_SINGLE_STEP (0x8000004): will manipulate execution (avoid the real
AmsiScanBuffer)
DebugBreak() or INT3: force 0x8000003 -> VEH1
AmsiScanBuffer(): raise 0x8000004 -> VEH2
DebugBreak()
VEH1: Set HW
BPOINT on
AmsiScanBuffer
Address
Execution of
AmsiScanBuffer
VEH2:
Manipulate
Execution and
Register
Profit
32. #BHMEA23 www.blackhatmea.com
|
|
CATCHING the VEH
● VECTORED_EXCEPTION_HANDLER function:
○ mov edx, DWORD PTR [eax] : EXCEPTION_RECORD in EDX
○ mov edx, DWORD PTR [edx] : EXCEPTION_CODE in EDX
○ cmp edx,0x80000003 : check if EXCEPTION_BREAKPOINT
33. #BHMEA23 www.blackhatmea.com
|
|
CONCLUSION
● We are seeing/will see more malwares and Red Team abusing Patchless Attacks by Hardware Breakpoints
● As Defenders, studying a technique to unmask its internals can provided more precise and effective detections:
○ in the case of Hardware BreakPoints we focus on cover:
■ the HWBP installation: SetThreadContext Internals, and how can leverage kernel visibility to track its
(usually suspicious) usage
● Plus: we show also how we can craft an OpSec variation of that, avoiding the usage of that API, by
leveraging OS internals mechanisms
■ the HWBP exception handling implementation: the Vectored Exception Handler mechanisms
● Never-ending chess game between Attackers & Defenders