In the ever-evolving game of cybersecurity, encrypted shellcode injection emerges as a formidable method to sidestep defenses.
This article unveils the “Caro Kann Defense”—a savvy technique designed to evade memory scans, drawing inspiration from the world of chess.
Dive in to uncover the strategy behind this stealthy approach. Encrypted shellcode Injection to avoid memory scans triggered from Kernel (ETWti / Kernel Callbacks).
Specific combinations of Windows APIs, e.g. for injection into a remote process can lead to a memory scan:
Typically, the scan can be triggered from Userland via hooks on the execute primitive such as
But more and more EDR vendors also tend to trigger scans from Kernel, for example after the Kernel Callback
PsSetCreateThreadNotifyRoutine() a scan could be triggered.
But what if there is no executable memory section with known malicious code? Well, no alert for an detection I guess.
- Inject encrypted payload into an
- Inject custom non shellcode into an
- Create a remote Thread on the second shellcode
- Sleep for an amount x (to avoid memory scans triggered by the execute primitive of Thread creation)
- Decrypt the first shellcode
- Protect the section from
- Make a direct
JMPto the known malicious shellcode
On linux, the PIC-Code was found to be compiled correctly with
version 10-win32 20220324 (GCC).
With that version installed, the shellcode can be compiled with a simple
make and extracted from the
.text section via
If you’d like to compile from Windows, you can use the following commands:
as -o adjuststack.o adjuststack_as.asm gcc ApiResolve.c -Wall -m64 -ffunction-sections -fno-asynchronous-unwind-tables -nostdlib -fno-ident -O2 -c -o ApiResolve.o -Wl,--no-seh gcc DecryptProtect.c -Wall -m64 -masm=intel -ffunction-sections -fno-asynchronous-unwind-tables -nostdlib -fno-ident -O2 -c -o decryptprotect.o -Wl,--no-seh ld -s adjuststack.o ApiResolve.o decryptprotect.o -o decryptprotect.exe gcc extract.c -o extract.exe extract.exe
You also need to have Nim installed for this PoC.
nimble install winim ptr_math
nim c -d:release -d=mingw -d:noRes CaroKann.nim # Cross compile nim c -d:release CaroKann.nim # Windows
Any payload can be XOR encrypted with the given
Usage: encrypter.exe input_file output_file
The encrypted payload can than be embedded in the PoC via the following line:
const shellcode = slurp"<encrypted.bin>"
OPSec Improvement Ideas
- Bypass Userland-Hooks for Injection (although not really needed, but for fun)
- Back Payload(s) by legitimate DLL (Module Stomping)
- Load C2-Dlls via the first Shellcode – which can avoid memory scans triggered by module loads
- Use ThreadlessInject or DLLNotificationInjection instead of Remote Thread Creation
- Should use Sleep encryption, otherwise the payload will get flagged later
- Should use Unhooking first or (in)direct Syscalls
- Should use Proxy module loading