A proof-of-concept User-Defined Reflective Loader (UDRL) which aims to recreate, integrate, and enhance Cobalt Strike's evasion features!
Contributor | Notable Contributions | |
---|---|---|
Bobby Cooke | @0xBoku | Project original author and maintainer |
Santiago Pecin | @s4ntiago_p | Reflective Loader major enhancements |
Chris Spehn | @ConsciousHacker | Aggressor scripting |
Joshua Magri | @passthehashbrwn | IAT hooking |
Dylan Tran | @d_tranman | Reflective Call Stack Spoofing |
James Yeung | @5cript1diot | Indirect System Calls |
The built-in Cobalt Strike reflective loader is robust, handling all Malleable PE evasion features Cobalt Strike has to offer. The major disadvantage to using a custom UDRL is Malleable PE evasion features may or may not be supported out-of-the-box.
The objective of the public BokuLoader project is to assist red teams in creating their own in-house Cobalt Strike UDRL. The project aims to support all worthwhile CS Malleable PE evasion features. Some evasion features leverage CS integration, others have been recreated completely, and some are unsupported.
Before using this project, in any form, you should properly test the evasion features are working as intended. Between the C code and the Aggressor script, compilation with different versions of operating systems, compilers, and Java may return different results.
NtProtectVirtualMemory
obfuscate "true"
with custom UDRL Aggressor script implementation.0x1000
bytes will be nulls.XGetProcAddress
for resolving symbolsKernel32.GetProcAddress
xLoadLibrary
for resolving DLL's base address & DLL LoadingTEB->PEB->PEB_LDR_DATA->InMemoryOrderModuleList
Kernel32.LoadLibraryA
Command | Option(s) | Supported |
---|---|---|
allocator | HeapAlloc, MapViewOfFile, VirtualAlloc | All supported via BokuLoader implementation |
module_x64 | string (DLL Name) | Supported via BokuLoader implementation. Same DLL stomping requirements as CS implementation apply |
obfuscate | true/false | HTTP/S beacons supported via BokuLoader implementation. SMB/TCP is currently not supported for obfuscate true. Details in issue. Accepting help if you can fix :) |
entry_point | RVA as decimal number | Supported via BokuLoader implementation |
cleanup | true | Supported via CS integration |
userwx | true/false | Supported via BokuLoader implementation |
sleep_mask | (true/false) or (Sleepmask Kit+true) | Supported. When using default "sleepmask true" (without sleepmask kit) set "userwx true". When using sleepmask kit which supports RX beacon.text memory (src47/Ekko ) set "sleepmask true" && "userwx false". |
magic_mz_x64 | 4 char string | Supported via CS integration |
magic_pe | 2 char string | Supported via CS integration |
transform-x64 prepend | escaped hex string |
BokuLoader.cna Aggressor script modification |
transform-x64 strrep | string string |
BokuLoader.cna Aggressor script modification |
stomppe | true/false | Unsupported. BokuLoader does not copy beacon DLL headers over. First 0x1000 bytes of virtual beacon DLL are 0x00
|
checksum | number | Experimental. BokuLoader.cna Aggressor script modification |
compile_time | date-time string | Experimental. BokuLoader.cna Aggressor script modification |
image_size_x64 | decimal value | Unsupported |
name | string | Experimental. BokuLoader.cna Aggressor script modification |
rich_header | escaped hex string | Experimental. BokuLoader.cna Aggressor script modification |
stringw | string | Unsupported |
string | string | Unsupported |
make
BokuLoader.cna
Aggressor scriptUse the Script Console
to ensure BokuLoader was implemented in the beacon build
Does not support x86 option. The x86 bin is the original Reflective Loader object file.
RAW
beacons works out of the box. When using the Artifact Kit for the beacon loader, the stagesize
variable must be larger than the default.Original Cobalt Strike String | BokuLoader Cobalt Strike String |
---|---|
ReflectiveLoader | BokuLoader |
Microsoft Base Cryptographic Provider v1.0 | 12367321236742382543232341241261363163151d |
(admin) | (tomin) |
beacon | bacons |
Kernel32.LoadLibraryExA
is called to map the DLL from diskKernel32.LoadLibraryExA
is DONT_RESOLVE_DLL_REFERENCES (0x00000001)
RX
or RWX
memory will exist in the heap if sleepmask kit is not used.Kernel32.CreateFileMappingA
& Kernel32.MapViewOfFile
is called to allocate memory for the virtual beacon DLL.NtAllocateVirtualMemory
, NtProtectVirtualMemory
ntdll.dll
will not detect these systemcalls.mov eax, r11d; mov r11, r10; mov r10, rcx; jmp r11
assembly instructions within its executable memory.0x1000
bytes of the virtual beacon DLL are zeros.DllNotificationInection is a POC of a new “threadless” process injection technique that works by utilizing the concept of DLL Notification Callbacks in local and remote processes.
An accompanying blog post with more details is available here:
https://shorsec.io/blog/dll-notification-injection/
DllNotificationInection works by creating a new LDR_DLL_NOTIFICATION_ENTRY in the remote process. It inserts it manually into the remote LdrpDllNotificationList by patching of the List.Flink of the list head and the List.Blink of the first entry (now second) of the list.
Our new LDR_DLL_NOTIFICATION_ENTRY will point to a custom trampoline shellcode (built with @C5pider's ShellcodeTemplate project) that will restore our changes and execute a malicious shellcode in a new thread using TpWorkCallback.
After manually registering our new entry in the remote process we just need to wait for the remote process to trigger our DLL Notification Callback by loading or unloading some DLL. This obviously doesn't happen in every process regularly so prior work finding suitable candidates for this injection technique is needed. From my brief searching, it seems that RuntimeBroker.exe and explorer.exe are suitable candidates for this, although I encourage you to find others as well.
This is a POC. In order for this to be OPSEC safe and evade AV/EDR products, some modifications are needed. For example, I used RWX when allocating memory for the shellcodes - don't be lazy (like me) and change those. One also might want to replace OpenProcess, ReadProcessMemory and WriteProcessMemory with some lower level APIs and use Indirect Syscalls or (shameless plug) HWSyscalls. Maybe encrypt the shellcodes or even go the extra mile and modify the trampoline shellcode to suit your needs, or at least change the default hash values in @C5pider's ShellcodeTemplate project which was utilized to create the trampoline shellcode.
WinDiff is an open-source web-based tool that allows browsing and comparing symbol, type and syscall information of Microsoft Windows binaries across different versions of the operating system. The binary database is automatically updated to include information from the latest Windows updates (including Insider Preview).
It was inspired by ntdiff and made possible with the help of Winbindex.
WinDiff is made of two parts: a CLI tool written in Rust and a web frontend written in TypeScript using the Next.js framework.
The CLI tool is used to generate compressed JSON databases out of a configuration file and relies on Winbindex
to find and download the required PEs (and PDBs). Types are reconstructed using resym
. The idea behind the CLI tool is to be able to easily update and regenerate databases as new versions of Windows are released. The CLI tool's code is in the windiff_cli
directory.
The frontend is used to visualize the data generated by the CLI tool, in a user-friendly way. The frontend follows the same principle as ntdiff
, as it allows browsing information extracted from official Microsoft PEs and PDBs for certain versions of Microsoft Windows and also allows comparing this information between versions. The frontend's code is in the windiff_frontend
directory.
A scheduled GitHub action fetches new updates from Winbindex
every day and updates the configuration file used to generate the live version of WinDiff. Currently, because of (free plans) storage and compute limitations, only KB and Insider Preview updates less than one year old are kept for the live version. You can of course rebuild a local version of WinDiff yourself, without those limitations if you need to. See the next section for that.
Note: Winbindex
doesn't provide unique download links for 100% of the indexed files, so it might happen that some PEs' information are unavailable in WinDiff because of that. However, as soon as these PEs are on VirusTotal, Winbindex
will be able to provide unique download links for them and they will then be integrated into WinDiff automatically.
The full build of WinDiff is "self-documented" in ci/build_frontend.sh
, which is the build script used to build the live version of WinDiff. Here's what's inside:
# Resolve the project's root folder
PROJECT_ROOT=$(git rev-parse --show-toplevel)
# Generate databases
cd "$PROJECT_ROOT/windiff_cli"
cargo run --release "$PROJECT_ROOT/ci/db_configuration.json" "$PROJECT_ROOT/windiff_frontend/public/"
# Build the frontend
cd "$PROJECT_ROOT/windiff_frontend"
npm ci
npm run build
The configuration file used to generate the data for the live version of WinDiff is located here: ci/db_configuration.json
, but you can customize it or use your own. PRs aimed at adding new binaries to track in the live configuration are welcome.
A DLL Loader With Advanced Evasive Features
"Atom"
function via the command line.CRC32
string hashing algorithm.AtomLdr's unhooking method looks like the following
the program Unhooking from the \KnwonDlls\ directory is not a new method to bypass user-land hooks. However, this loader tries to avoid allocating RWX memory when doing so. This was obligatory to do in KnownDllUnhook for example, where RWX permissions were needed to replace the text section of the hooked modules, and at the same time allow execution of functions within these text sections.
This was changed in this loader, where it suspends the running threads, in an attempt to block any function from being called from within the targetted text sections, thus eliminating the need of having them marked as RWX sections before unhooking, making RW permissions a possible choice.
This approach, however, created another problem; when unhooking, NtProtectVirtualMemory
syscall and others were using the syscall instruction inside of ntdll.dll module, as an indirect-syscall approach. Still, as mentioned above, the unhooked modules will be marked as RW sections, making it impossible to perform indirect syscalls, because the syscall instruction that we were jumping to, can't be executed now, so we had to jump to another executable place, this is where win32u.dll
was used.
win32u.dll
contains some syscalls that are GUI-related functions, making it suitable to jump to instead of ntdll.dll. win32u.dll is loaded (statically), but not included in the unhooking routine, which is done to insure that win32u.dll can still execute the syscall instruction we are jumping to.
The suspended threads after that are resumed.
It is worth mentioning that this approach may not be that efficient, and can be unstable, that is due to the thread suspension trick used. However, it has been tested with multiple processes with positive results, in the meantime, if you encountered any problems, feel free to open an issue.
PayloadConfig.pc
file, that contains the encrypted payload, and its encrypted key and iv.PayloadConfig.pc
file will then replace this in the AtomLdr
project.AtomLdr
project as x64 Release.AtomLdr.dll
using rundll32.exe, running Havoc payload, and capturing a screenshotAtomLdr.dll
's Import Address TablePayloadBuilder.exe
, to encrypt demon[111].bin
- a Havoc payload fileAtomLdr.dll
using rundll32.exeHavoc is a modern and malleable post-exploitation command and control framework, created by @C5pider.
Havoc is in an early state of release. Breaking changes may be made to APIs/core structures as the framework matures.
Consider supporting C5pider on Patreon/Github Sponsors. Additional features are planned for supporters in the future, such as custom agents/plugins/commands/etc.
Please see the Wiki for complete documentation.
Havoc works well on Debian 10/11, Ubuntu 20.04/22.04 and Kali Linux. It's recommended to use the latest versions possible to avoid issues. You'll need a modern version of Qt and Python 3.10.x to avoid build issues.
See the Installation guide in the Wiki for instructions. If you run into issues, check the Known Issues page as well as the open/closed Issues list.
Cross-platform UI written in C++ and Qt
Written in Golang
Havoc's flagship agent written in C and ASM
You can join the official Havoc Discord to chat with the community!
To contribute to the Havoc Framework, please review the guidelines in Contributing.md and then open a pull-request!
laZzzy is a shellcode loader that demonstrates different execution techniques commonly employed by malware. laZzzy was developed using different open-source header-only libraries.
Nt*
) functions (not all functions but most)\x90
)Windows machine w/ Visual Studio and the following components, which can be installed from Visual Studio Installer
> Individual Components
:
C++ Clang Compiler for Windows
and C++ Clang-cl for build tools
ClickOnce Publishing
Python3 and the required modules:
python3 -m pip install -r requirements.txt
(venv) PS C:\MalDev\laZzzy> python3 .\builder.py -h
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣤⣤⣤⠀⢀⣼⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀
⠀⠀⣿⣿⠀⠀⠀⠀⢀⣀⣀⡀⠀⠀⠀⢀⣀⣀⣀⣀⣀⡀⠀⢀⣼⡿⠁⠀⠛⠛⠒⠒⢀⣀⡀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⣰⣾⠟⠋⠙⢻⣿⠀⠀⠛⠛⢛⣿⣿⠏⠀⣠⣿⣯⣤⣤⠄⠀⠀⠀⠀⠈⢿⣷⡀⠀⣰⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⣿⣯ ⠀⠀⢸⣿⠀⠀⠀⣠⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣧⣰⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⠙⠿⣷⣦⣴⢿⣿⠄⢀⣾⣿⣿⣶⣶⣶⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀by: CaptMeelo⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠁⠀⠀⠀
usage: builder.py [-h] -s -p -m [-tp] [-sp] [-pp] [-b] [-d]
options:
-h, --help show this help message and exit
-s path to raw shellcode
-p password
-m shellcode execution method (e.g. 1)
-tp process to inject (e.g. svchost.exe)
-sp process to spawn (e.g. C:\\Windows\\System32\\RuntimeBroker.exe)
-pp parent process to spoof (e.g. explorer.exe)
-b binary to spoof metadata (e.g. C:\\Windows\\System32\\RuntimeBroker.exe)
-d domain to spoof (e.g. www.microsoft.com)
shellcode execution method:
1 Early-bird APC Queue (requires sacrificial proces)
2 Thread Hijacking (requires sacrificial proces)
3 KernelCallbackTable (requires sacrificial process that has GUI)
4 Section View Mapping
5 Thread Suspension
6 LineDDA Callback
7 EnumSystemGeoID Callback
8 FLS Callback
9 SetTimer
10 Clipboard
Execute builder.py
and supply the necessary data.
(venv) PS C:\MalDev\laZzzy> python3 .\builder.py -s .\calc.bin -p CaptMeelo -m 1 -pp explorer.exe -sp C:\\Windows\\System32\\notepad.exe -d www.microsoft.com -b C:\\Windows\\System32\\mmc.exe
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣤⣤⣤⠀⢀ ⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⠀⠀⢀⣀⣀⡀⠀⠀⠀⢀⣀⣀⣀⣀⣀⡀⠀⢀⣼⡿⠁⠀⠛⠛⠒⠒⢀⣀⡀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⣰⣾⠟⠋⠙⢻⣿⠀⠀⠛⠛⢛⣿⣿⠏⠀⣠⣿⣯⣤⣤⠄⠀⠀⠀⠀⠈⢿⣷⡀⠀⣰⣿⠃ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⣿⣯⠀⠀⠀⢸⣿⠀⠀⠀⣠⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣧⣰⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣿⣿⠀⠀⠙⠿⣷⣦⣴⢿⣿⠄⢀⣾⣿⣿⣶⣶⣶⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀by: CaptMeelo⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠁⠀⠀⠀
[+] XOR-encrypting payload with
[*] Key: d3b666606468293dfa21ce2ff25e86f6
[+] AES-encrypting payload with
[*] IV: f96312f17a1a9919c74b633c5f861fe5
[*] Key: 6c9656ed1bc50e1d5d4033479e742b4b8b2a9b2fc81fc081fc649e3fb4424fec
[+] Modifying template using
[*] Technique: Early-bird APC Queue
[*] Process to inject: None
[*] Process to spawn: C:\\Windows\\System32\\RuntimeBroker.exe
[*] Parent process to spoof: svchost.exe
[+] Spoofing metadata
[*] Binary: C:\\Windows\\System32\\RuntimeBroker.exe
[*] CompanyName: Microsoft Corporation
[*] FileDescription: Runtime Broker
[*] FileVersion: 10.0.22621.608 (WinBuild.160101.0800)
[*] InternalName: RuntimeBroker.exe
[*] LegalCopyright: © Microsoft Corporation. All rights reserved.
[*] OriginalFilename: RuntimeBroker.exe
[*] ProductName: Microsoft® Windows® Operating System
[*] ProductVersion: 10.0.22621.608
[+] Compiling project
[*] Compiled executable: C:\MalDev\laZzzy\loader\x64\Release\laZzzy.exe
[+] Signing binary with spoofed cert
[*] Domain: www.microsoft.com
[*] Version: 2
[*] Serial: 33:00:59:f8:b6:da:86:89:70:6f:fa:1b:d9:00:00:00:59:f8:b6
[*] Subject: /C=US/ST=WA/L=Redmond/O=Microsoft Corporation/CN=www.microsoft.com
[*] Issuer: /C=US/O=Microsoft Corporation/CN=Microsoft Azure TLS Issuing CA 06
[*] Not Before: October 04 2022
[*] Not After: September 29 2023
[*] PFX file: C:\MalDev\laZzzy\output\www.microsoft.com.pfx
[+] All done!
[*] Output file: C:\MalDev\laZzzy\output\RuntimeBroker.exe
Get fresh Syscalls from a fresh ntdll.dll copy. This code can be used as an alternative to the already published awesome tools NimlineWhispers and NimlineWhispers2 by @ajpc500 or ParallelNimcalls.
The advantage of grabbing Syscalls dynamically is, that the signature of the Stubs is not included in the file and you don't have to worry about changing Windows versions.
To compile the shellcode execution template run the following:
nim c -d:release ShellcodeInject.nim
The result should look like this:
ropr is a blazing fast multithreaded ROP Gadget finder
ROP (Return Oriented Programming) Gadgets are small snippets of a few assembly instructions typically ending in a ret
instruction which already exist as executable code within each binary or library. These gadgets may be used for binary exploitation and to subvert vulnerable executables.
When the addresses of many ROP Gadgets are written into a buffer we have formed a ROP Chain. If an attacker can move the stack pointer into this ROP Chain then control can be completely transferred to the attacker.
Most executables contain enough gadgets to write a turing-complete ROP Chain. For those that don't, one can always use dynamic libraries contained in the same address-space such as libc once we know their addresses.
The beauty of using ROP Gadgets is that no new executable code needs to be written anywhere - an attacker may achieve their objective using only the code that already exists in the program.
Typically the first requirement to use ROP Gadgets is to have a place to write your ROP Chain - this can be any readable buffer. Simply write the addresses of each gadget you would like to use into this buffer. If the buffer is too small there may not be enough room to write a long ROP Chain into and so an attacker should be careful to craft their ROP Chain to be efficient enough to fit into the space available.
The next requirement is to be able to control the stack - This can take the form of a stack overflow - which allows the ROP Chain to be written directly under the stack pointer, or a "stack pivot" - which is usually a single gadget which moves the stack pointer to the rest of the ROP Chain.
Once the stack pointer is at the start of your ROP Chain, the next ret
instruction will trigger the gadgets to be excuted in sequence - each using the next as its return address on its own stack frame.
It is also possible to add function poitners into a ROP Chain - taking care that function arguments be supplied after the next element of the ROP Chain. This is typically combined with a "pop gadget", which pops the arguments off the stack in order to smoothly transition to the next gadget after the function arguments.
Easy install:
cargo install ropr
the application will install to ~/.cargo/bin
From source:
git clone https://github.com/Ben-Lichtman/ropr
cd ropr
cargo build --release
the resulting binary will be located in target/release/ropr
Alternatively:
git clone https://github.com/Ben-Lichtman/ropr
cd ropr
cargo install --path .
the application will install to ~/.cargo/bin
rax
with a value from another register I may choose to filter by the regex ^mov eax, ...;
: Get fresh Syscalls from a fresh ntdll.dll copy. This code can be used as an alternative to the already published awesome tools NimlineWhispers and NimlineWhispers2 by @ajpc500 or ParallelNimcalls.
The advantage of grabbing Syscalls dynamically is, that the signature of the Stubs is not included in the file and you don't have to worry about changing Windows versions.
To compile the shellcode execution template run the following:
nim c -d:release ShellcodeInject.nim
The result should look like this: