Release v0.7 - The Pain of Tsukuyomi

Brute Ratel v0.7.0 (Tsukuyomi) is now available for download and provides a major update towards in-memory evasion and addition of open source tooling. This release also contains addition and conversion of several public BOFs to internal functions of Brute Ratel so that users won’t have to depend on public BOFs which might not necessarily be stable. This also helps to avoid VirtualAlloc API call for BOF allocation altogether which is an added cherry on the top. I have listed the technical details of the release below, however a detailed list on the features and bug fixes can be found in the release notes.

Feature Additions

Transition to NTAPIs and Syscalls

Whenever you build a new payload from the Ratel server, you get a file which directly or indirectly executes a reflective DLL of the badger. The loader of this badger was improved to purely use NTAPIs instead of WinAPIs to avoid getting flagged from the generic VirtualAlloc+VirtualProtect+Dllmain detections. All other memory allocations in the badger are also replaced with NTAPI calls. This also allowed the possibility to add NTAPI options for memory allocation in the set_malloc command. The set_malloc command now has another option to allocate memory via NtAllocateVirtualMemory, NtProtectVirtualMemory and NtWriteVirtualMemory which can be used for injections like reflective DLLs, C-sharp PEs and shellcode injections.

NOTE: This feature was upgraded in v0.8

The Ultimate Genjutsu - Tsukuyomi

Unlike other C2 frameworks, Badger is a multi-threaded application. If you execute multiple commands, they get executed simultaneously instead of sequentially. This helps to run multiple commands at the same time, while also making sure that if you execute a long running task, then your payload does not get stuck on one task waiting for the tasker thread to complete. This was all good until I started implementing Tsukuyomi - A technique where the badger moves itself from the RX to RW region, encrypts it, sleeps for an X number of seconds specified by the user, decrypts itself, moves back to RX region and then continues the whole execution of the main thread again. This is all good in theory for a single threaded application where you don’t have to worry about shared resources and actively running threads. For example, take a scenario where there are two long running tasks, and then the badger changes the RX region to RW, encrypts it and goes to sleep. This will eventually lead to a crash. Because the two threads which you started are running the instructions within the RX region, which is not executable now (since it’s RW now) and the instructions are not even logical because they are now encrypted. In order to circumvent this, I decided to suspend all the running threads except the main thread and then go to sleep. But this led to another major problem - DEADLOCKS!!!

There is a concept in Naruto, where you can trap someone in an illusion and force them to live out their eternity believing that illusion is real. This can be a painful illusion or a dream that you want to live in. This is called The Ultimate Genjutsu - Tsukuyomi. I particularly call this feature of badger as Tsukuyomi because it was a pain to implement. Ask any programmer and you will realize that deadlocks are the hardest to identify and fix, since its not exactly a programming error. The worst part of this is that deadlocks happen when you try to fix a race condition with Mutexes and it’s hard to recreate the bug. Think of a scenario where I start Thread-One - which is continuously allocating memory using calloc/malloc or HeapAlloc. Now the memory allocation APIs will eventually call RtlHeapAlloc followed by EnterCriticalSection which locks the process heap with a mutex.

This lock happens so that the process heap does not end up providing the same memory address for heap allocation to 2 different threads. When the code enters a critical section, every other thread which is trying to perform HeapAlloc/calloc/malloc will wait for Thread-One’s mutex to be released. But let’s say that while Thread-One has entered a critical section, the main thread which is going to sleep has suspended Thread-One. And now when the main thread tries to allocate some buffer on heap, it will wait for the CriticalSection mutex to be released by Thread-One. But Yay! Our Thread-One is suspended by our main thread which is waiting to be resumed, but it won’t be resumed until the main thread returns from sleep and then resumes the suspended thread. Here, both the threads are waiting for each other to complete and now we have a perfect Deadlock! However after spending a lot of time in windbg, x64dbg and reading a lot of documentation on msdn, I was able to eventually figure out the deadlock and fix it. That’s how I was able to break out of The Ultimate Genjutsu - Tsukuyomi.

Thus now, the Badger comes equipped with a default hide, encrypt and sleep (HES) option. This is applicable to not only badger’s process, but also any shellcode injection/reflective injections performed by the badger. A quick example of this can be see in the video below:

This feature was partially inspired by Austin Hudson’s Foliage, but foliage was extremely buggy, did not support multi-threading. A lot of work went into converting it’s concept into an actual working multi-threaded badger as it relies heavily on threading for several tasks.


The shadowcloak command is now moved from beta to stable however is only supporting x64 for the time being. The shadowcloak is also updated since the previous beta version had some issue with older versions of Windows. This is now fixed and runs flawlessly in all versions of windows including Windows 11. Most of the API calls required to dump the memory are written from scratch instead of calling the APIs from windlls. This helps to avoid calling of any API call which might be hooked. Shadowcloak will directly download the file from the memory and show you progress on the downloaded dump. This command takes a while to fully complete since it extracts the memory blocks and downloads them directly to the Ratel server.

Remote Process Enumeration

The ps_ex command can be used to enumerate processes on a target host. This command can be used alongside stolen or generated tokens to query a target host.

User Enumeration

The id and get_privs command is now deprecated and are merged with a new command userinfo. This command returns several information about the current user including the groups the user is a part of.

Session Enumeration

The local_sessions and query_session command can be used to enumerate users logged into the current or a target host. This can be extremely helpful to check if a user is logged-in to a specific target host and how they are logged in (powershell, rdp, console… etc). This command when combined with crisis_monitor and grab_token can be really powerful to get a notification as soon as a user logs in, validate the user and steal the token to further move laterally or execute some command from the stolen token.

Network Enumeration

Multiple network enumeration commands were added to the badger in this release. These include the following:

  • Routes - displays IPv4 routes for current host
  • ARP - displays ARP entries for all network interfaces
  • Dnscache - displays the DNS cache of current host
  • Netstat - displays all TCP/UDP connections and listening ports on current host

System Enumeration

Multiple system enumeration commands were added to the badger in this release. These include the following:

  • Sysinfo - displays basic system and hardware information
  • Windowlist - displays all hidden and visible windows
  • Getenv - displays all the environmental variables set for the current process

Starting Services

In one of my previous releases, I added scquery, scdelete and scdivert command, but I didn’t add any command for starting a service. That is now added to the badger with the scstart command.

Mass Share Enumeration

In one of my previous releases where I released the netshares command, I was bombarded with requests to add support for a mass share scan option instead of enumerating shares on a single host. Thus, I added the sharescan command which takes hostnames separated by newlines in a text file and enumerates the shares on them. This can be combined with other features like ldap_sentinel or sentinel to enumerate all the computers in a domain/forest and enumerate the shares on them.

Shellcode Injection

In one of my earlier releases, I added a feature to inject shellcode into a newly created child process using the shinject command. However, this command was limited to injecting the shellcode to a newly created child process. It did not have an option to inject the shellcode into an existing process that the user wanted. However, this is is no longer the case. The shinject_ex command takes a PID and a local path for a shellcode’s binary file and injects that into any given process via a PID (provided you have the right permissions). The shinject command still exists which can be used to create a new process with custom PPID, arguments and other techniques.

Optional ETW patching

While I was testing shellcode injection, I found that a lot of EDRs use ETW providers to detect process injection via NtQueueApcThread or QueueUserAPC. I thus added an individual command patchetw which can just patch ETW from ntdll.dll irrespective of the patching that happens when you use sharpinline or sharpreflect.

Scheduled Tasks Enumeration

The schtquery command added to this release can perform a full or basic enumeration of scheduled tasks on the current or a target host. The optional full argument enumerates and returns the XML information of the scheduled task.

Password Policy Enumeration

The passpol command can be used to enumerate the password policy of current or a target host. The optional argument takes in a target host to enumerate which doesn’t necessarily need a token of a user from the target system. Any domain user can use this against a host in a domain without any special privilege.

Keystroke Logging

Keylogger now comes in the form of a reflective DLL which can be injected to a remote process. This is basically a fork&run command. The keylogger command will inject the reflective DLL into a child process and start capturing the user’s keystrokes. To view the output and stop the keylogger, just kill the injected process.

Inline Ldap Query with ‘Sentinel’

LDAP sentinel is now implemented within the badger itself apart from also being available as a reflectively loaded module. Usually the LDAP Sentinel was built to be run as an RDLL which meant that it would run from the token/privileges of its parent process which created the injected process. However, I got several requests to add support for LDAP queries from the context of a different user. This is important especially when you are executing LDAP queries from a host which may be working over a VPN and you only have privileges of a local user instead of a domain user. In such scenarios (assumed breach), you would need to impersonate a user and run LDAP queries. So, I added the sentinel command which can run LDAP queries directly by using any token created by the user. This option is available as a separate command-line in the GUI or can be run from the badger’s terminal itself. When you select LDAP Sentinel from the context menu of a badger, even there, you will find an option whether you want LDAP queries to be run reflectively or from the badger itself.

  • Sentinel ( Inline Query - Commandline and Terminal)
  • LDAP Sentinel (Process Injected/Inline GUI Option)

The LDAP Sentinel’s code was also heavily updated and the executable size for the RDLL has gone down to 18kb instead of the earlier 38kb.

Server Accessibility

Another feature which was requested by a customer was an option for the payload to die if it was not able to connect to the C2 server on the first request. With this option, if the badger is unable to reach the server during its initial connection, it will simply exit without doing anything. This feature opened up the possibility for future releases to add backup covert C2 channels if the primary C2 channel fails. This option is available in the GUI when you create a new listener or payload profile.

Sharpinline Updates

Sharpinline which earlier ran in the main thread is now asynchronous. This means you can run shareinline without waiting for the output to return back and run other commands. Another feature which was actually missing in the previous release was to add your own C-sharp file as an internal command of BRc4 to be executed with sharpinline. This can be done using the register_pe_inline block in the json c4 profile.

"register_pe_inline": {
    "monologue": {
        "file_path": "server_confs/InternalMonologue.exe",
        "description": "Runs InternalMonologue C# executable",
        "artifact": "WINAPI",
        "mainArgs": "NA",
        "optionalArg": "NA",
        "example": "monologue",
        "minimumArgCount": 1


Several other existing commands and functions were improved with more metadata to offer. The ones listed below are the significant ones.

Enhanced Service Enumeration

The ‘scquery’ command was heavily updated in the current release. Service start/stop codes were also replaced with actual strings so that it would show whether the service has started or stopped instead of WinAPI error codes. Now it also accepts an additional ‘full/basic’ argument as a third argument which can show the service description and trigger information. Service triggers are not known to a lot of people in infosec which can used to identify when a service starts respectively. Some service start when a user joins a domain, while some start when there are changes to the network. Some of these service’s path can be modified and replaced with the service executable of badger, thus adding a backdoor to the host. Thus when a trigger relative to the service is initiated, our badger’s service will start providing us a callback in return.

More details on service trigger events can be found in the msdn documentation here.

Updated ‘net’ command which can now query users, groups and members of groups

The net command of the badger is now updated to provide options to enumerate user groups locally or remotely. This can be done by providing groups (for all groups) or group followed by a group name to enumerate members locally or over a domain.

Updated ‘drivers’ command to show metadata like company name and address loaded of the driver

The drivers command is now updated to show detailed information about drivers running on the current host. This can also provide information about the path of the driver and under which organization they are signed under by reviewing their certificates which can help enumerate EDRs on the current host. Below is an example of Microsoft’s ATP running on the badger’s host.

Updated ‘list_modules’ command with company name

Similar to the drivers command, the list_modules command also shows detailed information on the company name of the DLL loaded, their full path and their loaded addresses.

Updated ‘impersonate’ command to select user id instead of just usernames

The impersonate command is now updated to use the IDs of the usernames from Token vault instead of just specifing the usernames. This was an issue when the user’s name was not readable or was in a language apart from english and gibberish characters were showed on screen. With the current update, you can either use the username or the ID of the token stored in the token vault.

Updates to the ‘help’ command

Several users wanted to know which command affect other commands from brute ratel. For example, which commands are affected by make_token/impersonate commands. The updated ‘help’ command output now shows all of this information. For example, the set_parent command shows the commands which support set_parent are get_parent and clear_parent, whereas the affected commands show which commands are affected by configuring the set_parent command. For example, if you set set_parent 1234, then all the affected commands will use the PPID 123 as their parent.

Several other minor tweaks and changes were made to the server, commander and the badger as whole to make it faster and more stable. Detailed information can be found in the release notes link provided above. This release laid foundation to several other upcoming features like heap encryption while sleeping, alternate DNS channels and automation of sequential command response/request which will be found in the upcoming releases.