Jekyll2026-03-09T08:19:28+00:00https://bruteratel.com/feed.xmlBrute Ratel C4Badger doesn't care. It takes what it wants!Chetan Nayak[email protected]Release v2.3 - Flux2025-10-07T01:30:00+00:002025-10-07T01:30:00+00:00https://bruteratel.com/release/2025/10/07/Release-Flux<p>Brute Ratel v2.3 (codename Flux) is now available for download. A key focus of this release was complete redevelopment of the Badger implant using a custom-built compiler, designed to improve operational security (OpSec) and significantly reduce its memory footprint. During development, I noticed several limitations in the existing MinGW and Clang toolchains, particularly in how they handle certain memory regions and stack layouts. To address these issues, the MinGW/Clang compiler provided limited support, as ideally, these are not options that a general developer would ideally want to mess with. But the built-in optimizations were a tad bit problematic when dealing with my stealth requirements. Thus, I ended up creating a tailored variant, capable of optimizing the Badger’s compilation process to meet the required stealth requirements. As a result, the size of the full Badger implant has been reduced by 30% in this release, with further reductions expected to reach around 60% in the next iteration — all while retaining the full feature set.</p> <p>Due to the extended development cycle and the one-month delay already incurred, not all optimizations from the new compiler could be fully integrated into this release, however they are scheduled for inclusion in the upcoming version. It is strongly recommended to review the accompanying blog post and documentation before deploying the new release.</p> <h2 id="badger">Badger</h2> <h3 id="safe-http-requests">Safe HTTP Requests</h3> <p>A new option called <strong><em>safe_http</em></strong> has been added under the <strong><em>OpSec</em></strong> tab in listener and payload profiles. When enabled, this feature uses an extremely lightweight 2–3 Kilobyte PIC stub to send HTTP requests backed by a valid stack designed by the operator, while keeping the Badger’s entire code and heap encrypted. In practice, this means that during the request, neither the Badger nor its thread actually exists in memory until the HTTP transaction is fully parsed and completed. When the HTTP request is made, everything related to the badger is encrypted, making it extremely difficult to have yara detections as the PIC stub doesn’t have anything in it, that can be used to build a detection on. This approach helps mitigate certain detection techniques, particularly from EDRs that leverage ETW-based memory scans triggered by network activity through wininet or winhttp libraries. Do note that <strong><em>safe_http</em></strong> does not work with <strong><em>disable_http_telemetry</em></strong> option, since that feature is not required when <strong><em>safe_http</em></strong> is enabled.</p> <h3 id="advanced-async-bof-aab">Advanced Async BOF (AAB)</h3> <p>BOF execution in Brute Ratel has always been asynchronous, but until now it lacked a way to hide the Badger while the BOF ran. This release overhauls BOF behavior. Thanks to the custom compiler I developed, I was able to rewrite the BOF loader from the ground up to support new BOF APIs and a redesigned BOF loader.</p> <p>We’ve added a new command, <strong><em>coffexec_async</em></strong>, which is separate from the existing <strong><em>coffexec</em></strong>. <strong><em>coffexec_async</em></strong> runs independently of the Badger and exposes new APIs that enable the BOF to operate while the Badger remains hidden. The updated BOF APIs also provide improved stealth controls and sleep-masking interaction with the Badger. Advanced async BOFs can even run concurrently i.e. multiple BOFs can execute while the Badger stays masked and dormant.</p> <p>The core logic of the AAB would be:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;">Badger Main Thread { coffexec_async { void coffee(...) { BadgerForceSleep(dispatch, <span class="hljs-number" style="color: rgb(222, 147, 95);">60</span>); // induce forced sleep <span class="hljs-keyword" style="color: rgb(178, 148, 187);">for</span> <span class="hljs-number" style="color: rgb(222, 147, 95);">60</span> seconds BadgerDispatch(dispatch, <span class="hljs-string" style="color: rgb(181, 189, 104);">"Hello World 1"</span>); // <span class="hljs-keyword" style="color: rgb(178, 148, 187);">print</span> hello world <span class="hljs-number" style="color: rgb(222, 147, 95);">1</span> BadgerDispatch(dispatch, <span class="hljs-string" style="color: rgb(181, 189, 104);">"Hello World 2"</span>); // <span class="hljs-keyword" style="color: rgb(178, 148, 187);">print</span> hello world <span class="hljs-number" style="color: rgb(222, 147, 95);">2</span> BadgerWakeupAndSend(dispatch); // send buffered data instantly before proceeding ahead BadgerWakeupAndExit(dispatch); // Wake <span class="hljs-keyword" style="color: rgb(178, 148, 187);">up</span> the badger interrupting the above forced <span class="hljs-keyword" style="color: rgb(178, 148, 187);">sleep</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">and</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">to</span> badger<span class="hljs-string" style="color: rgb(181, 189, 104);">'s main thread } } }</span></pre> <p>// To clarify the pseudo code above, when the Badger executes an asynchronous BOF, the operator can invoke the <strong><em>BadgerForceSleep</em></strong> API to place the Badger into sleep masking mode. This allows any sensitive tasks to be executed entirely without the Badger being present in memory. Once the task is complete, the operator can terminate the async BOF using <strong><em>BadgerWakeupAndExit</em></strong>.</p> <p>Another API, <em><strong>BadgerWakeupAndSend</strong></em>, can be used in both <strong><em>coffexec</em></strong> and <strong><em>coffexec_async</em></strong> to pause BOF execution midway and send any buffered content queued via <strong><em>BadgerDispatch</em></strong>. However, calling <strong><em>BadgerWakeupAndSend</em></strong> within <strong><em>coffexec_async</em></strong> will wake the Badger and break its sleep mask, so it should be used carefully.</p> <p>A few additional notes:</p> <ul> <li>Asynchronous BOFs function differently from regular BOFs, and as of this release, <strong><em>BadgerSpoofStackFrame</em></strong> API is not yet supported within async BOFs. However, a working implementation has been identified and will be included in version 2.4.</li> <li>Async Badger functionality requires sleep masking to be enabled. If the Badger’s sleep value is set to zero and coffexec_async is invoked, a minimum 1-second sleep interval will automatically be applied before executing the async BOF.</li> </ul> <p>Further details on the new BOF APIs are provided below.</p> <h4 id="badgerforcesleep-async-only-api"><ins>BadgerForceSleep (async only API)</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerForceSleep</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch, DWORD seconds)</span></span>; </pre> <p>This API instructs the Badger to encrypt and conceal itself in memory. It returns TRUE if masking is successfully initiated, and FALSE otherwise. Masking only occurs when no other active tasks are running within the Badger. For example, if an asynchronous task such as <strong><em>sharpinline</em></strong> is active, invoking this API will not trigger sleep masking — the <strong><em>sharpinline</em></strong> thread must return to its designated execution context, and therefore, its executable region cannot be encrypted. The API accepts a parameter specifying the number of sleep seconds, independent of the Badger’s configured sleep/jitter settings. Once invoked, masking remains in effect until either the specified time expires or one of the wakeup APIs — <strong><em>BadgerWakeupAndExit</em></strong> or <strong><em>BadgerWakeupAndSend</em></strong> — is called. If this API is not executed within <strong><em>coffexec_async</em></strong>, the Badger will continue using its standard sleep masking behavior as defined by the operator’s <strong><em>sleep/jitter</em></strong> configuration. Essentially, this API overrides the existing sleep mask configuration and enforces a forced sleep state.</p> <h4 id="badgerwakeupandexit-async-only-api"><ins>BadgerWakeupAndExit (async only API)</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT VOID <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerWakeupAndExit</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch)</span></span>; </pre> <p>This API terminates the Badger’s sleep mask and returns control from the BOF to the Badger’s main thread. It is critical to invoke this API at the end of every asynchronous BOF you implement. Failing to call this API at the end of the <strong><em>coffee</em></strong> function will cause the Badger to terminate.</p> <h4 id="badgerwakeupandsend"><ins>BadgerWakeupAndSend</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT VOID <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerWakeupAndSend</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch)</span></span>; </pre> <p>This API is compatible with both <strong><em>coffexec</em></strong> and <strong><em>coffexec_async</em></strong> commands. It takes the data buffered by <strong><em>BadgerDispatch</em></strong> and instructs the Badger’s main thread to immediately transmit it to the Ratel server. If the Badger is currently in a sleep-masked state, invoking this API will temporarily break the sleep mask to allow the buffered content to be sent.</p> <h4 id="badgerstoptask"><ins>BadgerStopTask</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerStopTask</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch)</span></span>; </pre> <p>This API is supported by both <strong><em>coffexec</em></strong> and <strong><em>coffexec_async</em></strong> commands. It allows a BOF to check whether the operator has issued a <strong><em>stop_task</em></strong> command. By integrating this API into your code, you can detect when a stop request has been made—if the function returns TRUE, you can gracefully exit your routine or perform any necessary cleanup operations. This approach gives the operator greater control over task management without forcefully terminating a BOF thread, which could otherwise lead to memory leaks and compromise both OpSec and overall stability.</p> <h4 id="badgerdownloadbuffer"><ins>BadgerDownloadBuffer</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerDownloadBuffer</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch, CHAR *fileName, PVOID fileBuffer, <span class="hljs-keyword" style="color: rgb(178, 148, 187);">unsigned</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">long</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">long</span> fileSize)</span></span>; </pre> <p>This API is compatible with both <strong><em>coffexec</em></strong> and <strong><em>coffexec_async</em></strong> commands. It accepts multiple arguments — primarily the filename, the complete file buffer, and the actual file size to be uploaded to the Ratel server. The data is transmitted using the Badger’s main HTTP thread and is automatically sent once the BOF execution completes.</p> <h4 id="badgersettoken"><ins>BadgerSetToken</ins>:</h4> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">DECLSPEC_IMPORT VOID <span class="hljs-title" style="color: rgb(129, 162, 190);">BadgerSetToken</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(WCHAR** dispatch, HANDLE hToken)</span></span>; </pre> <p>This API is supported by both <strong><em>coffexec</em></strong> and <strong><em>coffexec_async</em></strong> commands. While <strong><em>BadgerSetToken</em></strong> is not a new API, this release introduces a minor update — it now expects two arguments: the first being <strong><em>dispatch</em></strong>, and the second the token value. This adjustment was implemented to ensure compatibility with the <strong><em>coffexec_async</em></strong> command. If you’re using this API in BOFs built with earlier BRc4 versions, it is recommended to update your code to reflect this change.</p> <h3 id="features-ported-to-async-bof">Features Ported To Async BOF</h3> <p>With the introduction of the new Async BOF framework, it made sense to make certain Badger features more autonomous. After gathering feedback and discussing ideas with our customers in the Discord channel, I decided to transition several commands to run as Async BOFs. This allows them to operate independently while the Badger remains in its sleep-masked state.</p> <ul> <li>crisis_monitor</li> <li>portscan</li> <li>samdump</li> <li>pth</li> <li>dcsync</li> <li>shadowcloak</li> </ul> <p>When executing any of the above commands, it’s recommended to configure the Badger with a sleep interval of at least 30 to 60 seconds. This ensures you gain the full benefit of the sleep masking feature during their execution. And in order to make it easy to understand how Async BOFs work, there is an example BOF present in the sample_config directory in the brc4 package named <strong><em>async_bof_test.c</em></strong>, which might be more helpful to operators.</p> <h3 id="other-qol-updates">Other QOL Updates:</h3> <ul> <li>Added a new b_count key to the Badger profile, which tracks the total number of connections the Badger has made to the server.</li> <li>Added the <strong><em>dump_sharpinline</em></strong> command to retrieve output from partially completed <strong><em>sharpInline</em></strong> executions.</li> <li>Added the <strong><em>stop_sharpinline</em></strong> command to terminate an active .NET task initiated by <strong><em>sharpInline</em></strong>.</li> <li>Updated <strong><em>sharpinline</em></strong> to support running multiple versions of dotnet to run simultaneously within the same Badger instance.</li> <li>Updated the <strong><em>samdump</em></strong> command so it no longer impersonates SYSTEM by default. Operators without ‘SYSTEM’ privileges can use <strong><em>get_system</em></strong> or their own privilege escalation methods before running samdump.</li> <li>Improved error handling — GetLastError, HRESULT, and NTSTATUS codes are now parsed by the Ratel server and displayed directly in the Badger terminal, instead of showing generic error messages.</li> <li>Updated command history behavior — the Badger terminal now logs invalid commands as well, instead of storing only successfully executed ones.</li> <li>Optimized memory usage for screen recording (<strong><em>record_screen</em></strong>) functionality</li> <li>Updated the Badger and Ratel server download mechanisms — when downloading a file with an existing name in the downloads directory, the old file is now deleted and replaced with the new one. Downloads are also performed on the main thread instead of a separate one.</li> <li>Added a validation check to detect if a BOF is already using a stomped module; if so, any concurrently executed BOF will skip the stomping process.</li> <li>Merged the <strong><em>memdump</em></strong> and <strong><em>shadowcloak</em></strong> commands — both functionalities are now available under <strong><em>shadowcloak</em></strong>.</li> <li>Simplified the <strong><em>impersonate</em></strong> command to accept only the token ID, rather than both username and ID.</li> <li>Improved the version mismatch dialog in Commander to make it less intrusive and more informative.</li> <li>Fixed an issue with <strong><em>net group</em></strong> where group names containing spaces were not accepted.</li> <li>Fixed a hardware breakpoint handling bug in <strong><em>coffexec</em></strong>.</li> <li>Fixed a <strong><em>schtquery</em></strong> issue where full task names containing spaces were not being read properly.</li> <li>Fixed a token parsing bug that required a double backslash.</li> <li>Resolved a rare DNS mutex issue that occurred when the server was terminated and restarted.</li> <li>Fixed a UI issue where the Process Manager and File Explorer windows, sometimes would not close properly.</li> <li>Removed ‘0.0.0.0’ from the internal IP list displayed in the Badger tab.</li> <li>Removed argument spoofing and lock screen features.</li> <li>Removed YARA rules from Litterbox.</li> <li>Removed legacy DLL and service executable detection logic.</li> </ul> <p>While this post highlights many of the major updates in the Flux release, several additional changes have intentionally been left out to keep certain features away from EDRs. A significant portion of this update also involved deep backend improvements that strengthen stability, performance (for socks/rportfwd), and OpSec capabilities.</p> <p>The next release will push things even further — with the new compiler framework designed to push Badger’s stealth and resilience against modern EDRs to an entirely new level. It’s going to be a crazy one.</p>Chetan Nayak[email protected]Brute Ratel v2.3 (codename Flux) is now available for download. A key focus of this release was complete redevelopment of the Badger implant using a custom-built compiler, designed to improve operational security (OpSec) and significantly reduce its memory footprint. During development, I noticed several limitations in the existing MinGW and Clang toolchains, particularly in how they handle certain memory regions and stack layouts. To address these issues, the MinGW/Clang compiler provided limited support, as ideally, these are not options that a general developer would ideally want to mess with. But the built-in optimizations were a tad bit problematic when dealing with my stealth requirements. Thus, I ended up creating a tailored variant, capable of optimizing the Badger’s compilation process to meet the required stealth requirements. As a result, the size of the full Badger implant has been reduced by 30% in this release, with further reductions expected to reach around 60% in the next iteration — all while retaining the full feature set.Release v2.2 - Rinnegan2025-05-15T01:30:00+00:002025-05-15T01:30:00+00:00https://bruteratel.com/release/2025/05/15/Release-Rinnegan<head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <p>Brute Ratel v2.2 (codename Rinnegan) is now available for download. This release introduces several new features to the badger, so it’s strongly recommended to review the private release videos, and the documentation before using it. The Badger component was rewritten to support several new evasion functionalities. The user interface - Commander has also undergone changes to the core and now supports all the latest Debian/Ubuntu/Kali distribution as the core is now QT6.9, instead of the earlier 5.15. This means there would be full support for Ubuntu 24.04 Out-Of-Box or any latest distro with Gnome/KDE/Xfce etc.</p> <p>In addition to the updates to the Badger showcased in the private 2.2 release video, this blog highlights the publicly shareable aspects of the release. These details have been carefully selected to ensure they do not compromise the confidentiality of sensitive Badger features, features that, in the past, have been leveraged by certain cybersecurity EDR vendors to develop detection signatures against the product.</p> <ul> <li>Removed YARA signatures targeting indirect syscall techniques.</li> <li>Implemented <strong>Task 65</strong> API to retrieve comprehensive API metadata associated with listeners and badgers.</li> <li>Eliminated the <strong>os_type</strong> field from listener and badger profiles.</li> <li>Renamed the <strong>-sp</strong> option to <strong>-sample</strong> in the Ratel server CLI for better clarity.</li> <li>Removed the <strong>artifact</strong> field from command metadata. Introduced <strong>detectionSeverity</strong> and <strong>detectionClass</strong> attributes in the help command within Commander to display telemetry data generated by the badger for each command.</li> <li>The local IP field in the Commander used to display all IP address of the host on which the badger was executed. Its been reworked to display only the IP address which is used to establish the server connection. Other host IPs can still be retrieved using the <strong>ipstats</strong> command.</li> <li>Excluded <strong>ws2_32.dll</strong> from the default set of preloaded libraries.</li> <li>Resolved a server crash issue caused by malformed keying responses.</li> <li>Modified the <strong>memexec</strong> command-line hooking strategy for improved stealth.</li> <li>Corrected the <strong>scstart</strong> command to use only the <code class="language-plaintext highlighter-rouge">SERVICE_START</code> access flag when initiating services.</li> <li>Renamed server_confs to sample_config for consistency and clarity.</li> <li>Introduced a global dispatch variable (<strong>g_dispatch</strong>) in badger_exports.h to be used with <strong>BadgerDispatch</strong> and <strong>BadgerDispatchW</strong>, eliminating the need to explicitly pass dispatch to every function.</li> <li>Added new BOF APIs: <strong>BadgerAddPrivilege</strong> and <strong>BadgerSetToken</strong>. (See documentation for usage details.)</li> <li>Integrated 32 new API scripts along with a new Python library <strong>bruteratel.py</strong>, featuring asyncio-based WebSocket support to streamline Ratel API interaction.</li> <li>Upgraded Commander to Qt version 6.9.</li> <li>Revised default LDAP Sentinel queries to minimize YARA detection risks.</li> <li>Updated <strong>sharpinline</strong> to evade stack-based detections in specific CLR invocation scenarios.</li> <li>Added a conditional check in <strong>sharpinline</strong> to detect the presence of a custom AMSI.dll. If detected, AMSI function hooking is bypassed to avoid interference. (customer request)</li> <li>Introduced four new commands — <strong>set_dotnet_store</strong>, <strong>clear_dotnet_store</strong>, <strong>list_dotnet_store</strong>, and <strong>sharpinline_dotnet_store</strong> — enabling secure in-memory storage and execution of up to 10 encrypted .NET assemblies without repeated dotnet file uploads. (See documentation for usage details.)</li> <li>Added <strong>set working_hours</strong>, <strong>get working_hours</strong>, <strong>clear working_hours</strong> commands to configure badger callbacks during specific hours and days of the week (Sunday to Saturday).</li> </ul> <p>Several heavy internal changes were made to the badger’s architecture to make it harder to detect. However, one of the main highlight of this release is the python library ‘bruteratel.py’. This library, alongside the 32 API scripts are now available in the <strong>api</strong> directory within the BRc4 package. The scripts released with this package are:</p> <table> <tbody> <tr> <td>api_add_badger_profile.py</td> <td>api_build_stage.py</td> <td>api_list_badger_profile.py</td> <td>api_list_listener.py</td> </tr> <tr> <td>api_add_badger.py</td> <td>api_download_file.py</td> <td>api_list_badgers.py</td> <td>api_manage_autoruns.py</td> </tr> <tr> <td>api_add_listener.py</td> <td>api_host_file.py</td> <td>api_list_cmd_queue.py</td> <td>api_manage_clickscript.py</td> </tr> <tr> <td>api_build_badger.py</td> <td>api_list_archive.py</td> <td>api_list_hosted.py</td> <td>api_manage_creds.py</td> </tr> <tr> <td>api_manage_stager.py</td> <td>api_mitre_activity.py</td> <td>api_remove_badger_profile.py</td> <td>api_remove_badger.py</td> </tr> <tr> <td>api_remove_cmd_queue.py</td> <td>api_remove_hosted_file.py</td> <td>api_remove_listener.py</td> <td>api_riot_control.py</td> </tr> <tr> <td>api_send_badger_cmd.py</td> <td>api_server_backup.py</td> <td>api_user_activity.py</td> <td> </td> </tr> </tbody> </table> <p>All these APIs are self-explanatory when executed. For example, the <strong>api_list_listener.py</strong>, when executed returns the following information:</p> <pre style="font-family:monospace;color: rgb(197, 200, 198); background-color: rgb(29, 31, 33); font-weight: 400; ">ninja@darkvortex: python3 api_list_listener.py -h usage: api_list_listener.py [-h] -user -password -handler List available listeners on the ratel server optional arguments: -h, --<span style="color: rgb(222, 147, 95); font-weight: 400;">help</span> show this <span style="color: rgb(222, 147, 95); font-weight: 400;">help</span> message and <span style="color: rgb(222, 147, 95); font-weight: 400;">exit</span> -user api server username -password api server password -handler api server handler host and port. Eg: 127.0.0.1:8443 Example: python3 api_list_listener.py -user ninja -password pass@123 -handler 172.16.219.1:8443 ninja@darkvortex: python3 api_list_listener.py -u ninja -p pass@123 -handler 172.16.219.1:8443 [+] Authentication success [+] listeners: - doh-c2 - primary-c2</pre> <p>The API scripts serve as lightweight wrappers around the core <strong>bruteratel.py</strong> library. A quick inspection reveals that the script simply imports the <strong>bruteratel</strong> module and invokes the <strong>br_connect_handler</strong> and <strong>br_list_listeners</strong> functions from it.</p> <pre style="font-family:monospace;color: rgb(197, 200, 198); background-color: rgb(29, 31, 33); font-weight: 400; "><span style="color: rgb(129, 162, 190); font-weight: 400;">import</span> argparse <span style="color: rgb(129, 162, 190); font-weight: 400;">import</span> asyncio <span style="color: rgb(129, 162, 190); font-weight: 400;">import</span> bruteratel <span style="color: rgb(129, 162, 190); font-weight: 400;">async</span> <span style="color: rgb(129, 162, 190); font-weight: 400;">def</span> <span style="color: rgb(240, 198, 116); font-weight: 400;">main</span>(): parser = argparse.ArgumentParser( description=<span style="color: rgb(181, 189, 104); font-weight: 400;">"List available listeners on the ratel server"</span>, epilog=<span style="color: rgb(181, 189, 104); font-weight: 400;">"Example:\n python3 api_list_listener.py -user ninja -password pass@123 -handler 172.16.219.1:8443"</span>, formatter_class=argparse.RawTextHelpFormatter ) parser.add_argument(<span style="color: rgb(181, 189, 104); font-weight: 400;">'-user'</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">type</span>=<span style="color: rgb(222, 147, 95); font-weight: 400;">str</span>, required=<span style="color: rgb(204, 102, 102); font-weight: 400;">True</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">help</span>=<span style="color: rgb(181, 189, 104); font-weight: 400;">"api server username"</span>, metavar=<span style="color: rgb(181, 189, 104); font-weight: 400;">''</span>) parser.add_argument(<span style="color: rgb(181, 189, 104); font-weight: 400;">'-password'</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">type</span>=<span style="color: rgb(222, 147, 95); font-weight: 400;">str</span>, required=<span style="color: rgb(204, 102, 102); font-weight: 400;">True</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">help</span>=<span style="color: rgb(181, 189, 104); font-weight: 400;">"api server password"</span>, metavar=<span style="color: rgb(181, 189, 104); font-weight: 400;">''</span>) parser.add_argument(<span style="color: rgb(181, 189, 104); font-weight: 400;">'-handler'</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">type</span>=<span style="color: rgb(222, 147, 95); font-weight: 400;">str</span>, required=<span style="color: rgb(204, 102, 102); font-weight: 400;">True</span>, <span style="color: rgb(222, 147, 95); font-weight: 400;">help</span>=<span style="color: rgb(181, 189, 104); font-weight: 400;">"api server handler host and port. Eg: 127.0.0.1:8443"</span>, metavar=<span style="color: rgb(181, 189, 104); font-weight: 400;">''</span>) args = parser.parse_args() wsClient = <span style="color: rgb(129, 162, 190); font-weight: 400;">await</span> bruteratel.br_connect_handler(args.user, args.password, args.handler) <span style="color: rgb(222, 147, 95); font-weight: 400;">print</span>(<span style="color: rgb(181, 189, 104); font-weight: 400;">"[+] Authentication success"</span>) listenerlist = <span style="color: rgb(129, 162, 190); font-weight: 400;">await</span> bruteratel.br_list_listeners(wsClient) <span style="color: rgb(129, 162, 190); font-weight: 400;">if</span> listenerlist <span style="color: rgb(129, 162, 190); font-weight: 400;">is</span> <span style="color: rgb(129, 162, 190); font-weight: 400;">not</span> <span style="color: rgb(204, 102, 102); font-weight: 400;">None</span>: <span style="color: rgb(222, 147, 95); font-weight: 400;">print</span>(<span style="color: rgb(181, 189, 104); font-weight: 400;">"[+] listeners:"</span>) <span style="color: rgb(129, 162, 190); font-weight: 400;">for</span> i <span style="color: rgb(129, 162, 190); font-weight: 400;">in</span> listenerlist: <span style="color: rgb(222, 147, 95); font-weight: 400;">print</span>(<span style="color: rgb(181, 189, 104); font-weight: 400;">" - "</span>, i) <span style="color: rgb(129, 162, 190); font-weight: 400;">else</span>: <span style="color: rgb(222, 147, 95); font-weight: 400;">print</span>(<span style="color: rgb(181, 189, 104); font-weight: 400;">"[-] Error listing listeners"</span>) <span style="color: rgb(129, 162, 190); font-weight: 400;">await</span> wsClient.close(<span style="color: rgb(204, 102, 102); font-weight: 400;">1000</span>) <span style="color: rgb(129, 162, 190); font-weight: 400;">if</span> __name__ == <span style="color: rgb(181, 189, 104); font-weight: 400;">"__main__"</span>: asyncio.run(main()) </pre> <p>The <strong>br_connect_handler</strong> function accepts the username, password, and connection details, and uses AsyncIO to establish a WebSocket connection with the Ratel server. Upon successful connection, it returns a WebSocket client object, which can be passed to other functions within the library to perform various actions. Each function may return a different type of output depending on its purpose.</p> <p>This is as hard as it gets to call a function from the <strong>bruteratel</strong> library. The other features are described in more detail in the private discord channel of BRc4 and the documentation. Stay tuned and Happy Hacking.</p>Chetan Nayak[email protected]Exception Junction - Where All Exceptions Meet Their Handler2024-10-20T18:31:00+00:002024-10-20T18:31:00+00:00https://bruteratel.com/research/2024/10/20/Exception-Junction<p>This blog is in relation to some of the hurdles I’ve met while debugging and researching various new features for Brute Ratel. Before we get started, let me inform you that this blog is not for beginners. It requires some knowledge about Windows internals, exception handlers, and getting your hands dirty with a debugger, preferably x64dbg. And to add to that, there’s limited to near zero information on the web related to this topic, thus I spent the last 24 hours researching and writing this from scratch while being high on caffeine.</p> <p>There are two parts to this blog. The first part contains the ‘what’, ‘how’, and ‘why’ I reached here, and the second part focuses on the solution. And before we get started, I would like to thank Elastic EDR for making my life this hard :).</p> <h2 id="part-i-the-what">Part I: ‘The What?’</h2> <h3 id="what-are-exception-handlers">What are exception handlers?</h3> <p>There are multiple types of exception handlers, but we will be focusing on Vectored Exceptions (VEH). Both exception handlers (general) and vectored exception handlers in Windows serve the purpose of handling exceptions, such as access violations or divide-by-zero errors. However, they differ in their mechanisms, priority, and use cases. Structured Exception Handling (SEH) is the default mechanism for handling exceptions in Windows programs. An exception handler is registered within a specific function using constructs like ‘<code class="language-plaintext highlighter-rouge">__try / __except</code>’ mechanism. SEH is local to the function and cannot handle all exceptions globally unless explicitly configured via an exception filter.</p> <p>A vectored exception handler (VEH) is a global exception handler mechanism introduced in Windows. Unlike SEH, vectored handlers are not stack-based but registered globally within the process using ‘<code class="language-plaintext highlighter-rouge">kernel32!AddVectoredExceptionHandler</code>’/’<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler)</code>’. Vectored handlers are called before SEH and multiple vectored handlers can exist, unlike SEH, which is limited by stack frames.</p> <h2 id="part-ii-the-how">Part II: ‘The How?’</h2> <h3 id="how-did-i-reach-here">How did I reach here?</h3> <p>Vectored exception handlers can be used for a variety of purposes. In my case, it was mostly related to debugging and anti-debugging. Brute Ratel is an extremely large project and one of the main tasks for the release of 2.1 was to make sure every NTAPI function call that goes to the kernel has a valid call stack. While building a call stack is easy, finding every Windows API function used by the Badger, and pivoting them via call stack spoofing was a tedious task. I rewrote one of my old code for Process Instrumentation hook back from 2021 and built a DLL which can be loaded before the start of any process, for dumping information about every NTAPI-&gt;Syscall being performed. Here is the <a href="https://github.com/paranoidninja/PI-Tracker">PI Tracker</a> code:</p> <p>The NTAPI-Syscall Tracker (pi-tracker.c):</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">include</span> <span class="hljs-string" style="color: rgb(181, 189, 104);">"windows.h"</span></span> <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">include</span> <span class="hljs-string" style="color: rgb(181, 189, 104);">"stdio.h"</span></span> <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">define</span> EXPORT __declspec(dllexport)</span> HANDLE hModule; <span class="hljs-function">EXPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHookEnable</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span></span>; <span class="hljs-function">EXPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHookDisable</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span></span>; <span class="hljs-function">BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHook</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(BOOL enable)</span></span>; <span class="hljs-function">VOID <span class="hljs-title" style="color: rgb(129, 162, 190);">GetSyscallName</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(FARPROC SyscallRet)</span></span>; <span class="hljs-function"><span class="hljs-keyword" style="color: rgb(178, 148, 187);">extern</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">void</span> <span class="hljs-title" style="color: rgb(129, 162, 190);">hookedCallback</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span></span>; <span class="hljs-function"><span class="hljs-keyword" style="color: rgb(178, 148, 187);">extern</span> NTSTATUS <span class="hljs-title" style="color: rgb(129, 162, 190);">NtSetInformationProcess</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span></span>; <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">ifndef</span> NT_SUCCESS</span> <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">define</span> NT_SUCCESS(status) ((NTSTATUS) (status) &gt;= 0)</span> <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-meta" style="color: rgb(222, 147, 95);">#<span class="hljs-meta-keyword">define</span> ProcessInstrumentationCallback 0x28</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">typedef</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION { ULONG Version; ULONG Reserved; PVOID Callback; } PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION, *PPROCESS_INSTRUMENTATION_CALLBACK_INFORMATION; <span class="hljs-function">EXPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHookEnable</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span> </span>{ <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> PIHook(TRUE); } <span class="hljs-function">EXPORT BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHookDisable</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span> </span>{ <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> PIHook(FALSE); } <span class="hljs-function">BOOL <span class="hljs-title" style="color: rgb(129, 162, 190);">PIHook</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(BOOL enable)</span> </span>{ PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION InstrumentationCallbackInfo; InstrumentationCallbackInfo.Version = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; InstrumentationCallbackInfo.Reserved = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; InstrumentationCallbackInfo.Callback = <span class="hljs-literal" style="color: rgb(222, 147, 95);">NULL</span>; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (enable) { InstrumentationCallbackInfo.Callback = hookedCallback; } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (NT_SUCCESS(NtSetInformationProcess((HANDLE)<span class="hljs-number" style="color: rgb(222, 147, 95);">-1</span>, ProcessInstrumentationCallback, &amp;InstrumentationCallbackInfo, <span class="hljs-keyword" style="color: rgb(178, 148, 187);">sizeof</span>(InstrumentationCallbackInfo)))) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> TRUE; } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> FALSE; } <span class="hljs-function">VOID <span class="hljs-title" style="color: rgb(129, 162, 190);">GetSyscallName</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(FARPROC SyscallRet)</span> </span>{ PIHook(FALSE); FARPROC funcPtr = SyscallRet - <span class="hljs-number" style="color: rgb(222, 147, 95);">0x14</span>; BYTE* baseAddress = (BYTE*)hModule; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">char</span>* functionName = <span class="hljs-literal" style="color: rgb(222, 147, 95);">NULL</span>; IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)baseAddress; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (dosHeader-&gt;e_magic != IMAGE_DOS_SIGNATURE) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">goto</span> cleanUp; } IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(baseAddress + dosHeader-&gt;e_lfanew); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (ntHeaders-&gt;Signature != IMAGE_NT_SIGNATURE) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">goto</span> cleanUp; } IMAGE_OPTIONAL_HEADER* optionalHeader = &amp;ntHeaders-&gt;OptionalHeader; IMAGE_DATA_DIRECTORY* exportDataDir = &amp;optionalHeader-&gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (exportDataDir-&gt;VirtualAddress == <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">goto</span> cleanUp; } IMAGE_EXPORT_DIRECTORY* exportDirectory = (IMAGE_EXPORT_DIRECTORY*)(baseAddress + exportDataDir-&gt;VirtualAddress); DWORD* funcAddressArray = (DWORD*)(baseAddress + exportDirectory-&gt;AddressOfFunctions); DWORD* nameArray = (DWORD*)(baseAddress + exportDirectory-&gt;AddressOfNames); WORD* ordinalArray = (WORD*)(baseAddress + exportDirectory-&gt;AddressOfNameOrdinals); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">for</span> (DWORD i = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; i &lt; exportDirectory-&gt;NumberOfFunctions; i++) { FARPROC currentFunction = (FARPROC)(baseAddress + funcAddressArray[i]); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (currentFunction == funcPtr) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">for</span> (DWORD j = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; j &lt; exportDirectory-&gt;NumberOfNames; j++) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (ordinalArray[j] == i) { functionName = (<span class="hljs-keyword" style="color: rgb(178, 148, 187);">char</span>*)(baseAddress + nameArray[j]); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">goto</span> cleanUp; } } } } cleanUp: <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (functionName) { <span class="hljs-built_in" style="color: rgb(222, 147, 95);">printf</span>(<span class="hljs-string" style="color: rgb(181, 189, 104);">"[PI-TRACKER] %s (%p)\n"</span>, functionName, funcPtr); } PIHook(TRUE); } <span class="hljs-function">BOOL WINAPI <span class="hljs-title" style="color: rgb(129, 162, 190);">DllMain</span><span class="hljs-params" style="color: rgb(222, 147, 95);">(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)</span> </span>{ <span class="hljs-keyword" style="color: rgb(178, 148, 187);">switch</span> (dwReason){ <span class="hljs-keyword" style="color: rgb(178, 148, 187);">case</span> DLL_PROCESS_ATTACH: { hModule = GetModuleHandleA(<span class="hljs-string" style="color: rgb(181, 189, 104);">"ntdll"</span>); PIHookEnable(); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">break</span>; } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">case</span> DLL_PROCESS_DETACH: PIHookDisable(); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">break</span>; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">case</span> DLL_THREAD_ATTACH: <span class="hljs-keyword" style="color: rgb(178, 148, 187);">case</span> DLL_THREAD_DETACH: <span class="hljs-keyword" style="color: rgb(178, 148, 187);">break</span>; } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> TRUE; }</pre> <p>The Hook (hook.asm):</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-meta" style="color: rgb(222, 147, 95);">section</span> .text <span class="hljs-meta" style="color: rgb(222, 147, 95);">global</span> hookedCallback: <span class="hljs-meta" style="color: rgb(222, 147, 95);">extern</span> GetSyscallName <span class="hljs-symbol" style="color: rgb(181, 189, 104);"> hookedCallback:</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">push</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">rcx</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">push</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">r10</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">mov</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">rcx</span>, <span class="hljs-built_in" style="color: rgb(222, 147, 95);">r10</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">call</span> GetSyscallName <span class="hljs-keyword" style="color: rgb(178, 148, 187);">pop</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">r10</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">pop</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">rcx</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">jmp</span> <span class="hljs-built_in" style="color: rgb(222, 147, 95);">r10</span></pre> <p>The MakeFile:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-section" style="color: rgb(129, 162, 190);">make:</span> nasm -f win64 hook.asm -o hook.o x86_64-w64-mingw32-gcc pi_tracker.c hook.o -o PI-Tracker.dll -s -O2 -lntdll -lkernel32 -DBUILD_DLL -shared </pre> <p>I won’t be deep diving into the above code, because it’s kinda straightforward. A brief overview would be, that the PI-Tracker.c file, has a Dllmain function that enables ‘<code class="language-plaintext highlighter-rouge">NTAPI-&gt;Syscall</code>’ tracking whenever it is loaded. It calls ‘<code class="language-plaintext highlighter-rouge">NtSetInformationProcess</code>’ API call with a callback hook and ‘<code class="language-plaintext highlighter-rouge">ProcessInstrumentationCallback</code>’ class. Once this is executed, every time an NTAPI-&gt;Syscall is called, before returning from the kernel to the return address of the syscall, the kernel makes a jump to the userland callback hook (‘<code class="language-plaintext highlighter-rouge">hookedCallback</code>’). One thing I noticed when this hook was executed, was that the ‘<code class="language-plaintext highlighter-rouge">R10</code>’ register contains the original return address of the syscall. And since every syscall return address (in Windows 10) is 0x14 bytes away from the actual NTAPI instruction, I can just subtract and find the NTAPI pointer. Once I have this, I can walk through the Export Address Table (EAT) of the ‘<code class="language-plaintext highlighter-rouge">ntdll.dll</code>’ to find which API was called by doing an ordinal comparison.</p> <p>Once this DLL is compiled, it can be loaded into any process using ‘<code class="language-plaintext highlighter-rouge">LoadLibraryA("pi-tracker.dll")</code>’ API call and it will dump all the NTAPI-&gt;Syscalls being called. Brute Ratel uses Vectored handlers for a variety of tasks from anti-debugging to walking some EDR’s DLL and finding the original overwritten syscall values etc. You can enable Vectored Exception using ‘<code class="language-plaintext highlighter-rouge">kernel32!AddVectoredExceptionHandler</code>’ which calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’. While tracking this API call, I found that ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ calls ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’. Now this was kind of concerning to me because Brute Ratel spoofs the stack of every ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’, but not the first execution of ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ as I use this API for some parts of call stack spoofing. So the problem is that the first execution of ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ will run with an unbacked stack which would call ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ creating a problem for an EDR like Elastic (with stack rules loaded) which relies heavily on call stack analysis. I come from a background where I spent my initial days reversing on Windows 7, and this part of calling ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ was not present in Windows 7. Being curious, I decided to see ‘WHY’ ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ calls ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ and if that can be avoided as this isn’t present on the previous versions of Windows. So if I could rewrite the entire ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ from scratch and avoiding the call of ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ like in Windows 7, then my problem is solved. And for those of you folks, who would plan to visit unknowncheats.me or reactos for this, let me inform you the entire code for that is buggy and does not support anything post the earlier versions of Windows 7.</p> <h2 id="part-iii-the-why">Part III: ‘The Why?’</h2> <h3 id="why-did-i-reach-here">Why did I reach here?</h3> <p>So to understand why the ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ is being called, I decided to reverse the entirety of the ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ API call. There are a lot of low-level structure modifications that I just built on the fly through my logic, and they could be wrong. But it works across various versions of Windows, so don’t fix what’s not broken I guess. I decided to download the symbols for ntdll in x64dbg so that I understand the internals properly. Note that any function that starts with ‘<code class="language-plaintext highlighter-rouge">Ldrp/Rtlp</code>’ is an internal function of the ntdll and does not have an export. They can only be viewed by downloading the PDB symbols, to call them, you need to perform a pattern-based search which could change over different versions of ‘<code class="language-plaintext highlighter-rouge">ntdll.dll</code>’ (more on this later at the end of the blog).</p> <p>The below figure shows that calling ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlpAddVectoredHandler</code>’ (notice the jump). There are two arguments to ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ which reside in RCX and RDX register. The first argument (RCX) ideally contains a ULONG value which specifies whether the handler should be added to the start of a LinkedList or the end. A non-zero value will add the new handler to the start and vice versa. In the below image, I am passing ‘<code class="language-plaintext highlighter-rouge">1</code>’ to RCX and my function pointer as a VectoredHandler to ‘<code class="language-plaintext highlighter-rouge">RDX</code>’. My VectoredHandler is built to simply catch the exception, print that caught it, and exit.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/vector_init.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/vector_init.png" /> </a> </div> </div> <p>The ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlpAddVectoredHandler</code>’ API calls ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrEnsureMrdataHeapExists</code>’ to check if a heap was already created on a previous iteration of ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ call. If this is the first call to ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’, then ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrEnsureMrdataHeapExists</code>’ returns False. ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrEnsureMrdataHeapExists</code>’ also stores a LinkedList (‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’) built on heap. If this LinkedList does not exist, it returns False, else True. ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrEnsureMrdataHeapExists</code>’ also calls ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrControlFlowGuardEnforced</code>’ to make sure this is not being exploited by some rop-gadget and is a legitimate call. When ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrControlFlowGuardEnforced</code>’ returns TRUE, it indicates that CFG is enforced, meaning that the system verifies indirect call targets to ensure they are legitimate before the call happens. This function typically checks the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrSystemDllInitBlock</code>’, which contains CFG-related flags and bitmap data to determine whether CFG is active for the current process or module. ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrControlFlowGuardEnforced</code>’ is called multiple times across this code to ensure rop-gadgets are not exploited.</p> <p>After checking the CFG, it calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlQueryProtectedPolicy</code>’ with GUID ‘<code class="language-plaintext highlighter-rouge">{ 0x1FC98BCA, 0x1BA9, 0x4397, {0x93, 0xF9, 0x34, 0x9E, 0xAD, 0x41, 0xE0, 0x57} }</code>’ to query if VEH is enabled. If not, it returns STATUS_NOT_FOUND. The below image shows the call to ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlQueryProtectedPolicy</code>’ with the first argument (RCX) as the pointer to the GUID ‘<code class="language-plaintext highlighter-rouge">CA 8B C9 1F A9 1B 97 43 93 F9 34 9E AD 41 E0 57</code>’ in the dump section below.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/query_policy.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/query_policy.png" /> </a> </div> </div> <p>The ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrControlFlowGuardEnforced</code>’ is called again to perform CFG validation.</p> <p>Next, it calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAllocateHeap</code>’ twice. The first call allocates Heap (we will call it FirstHeap - Address:0xE7AD0) of 0x28 (40) bytes. The second Allocation (SecondHeap - Address:0xE3910) is of 8 bytes. If you see the image below, once the FirstHeap (0xE7AD0) is allocated, the FirstHeap (0xE7AD0) pointer is moved to ‘RBX’ from ‘RAX’ and then the 0x18th offset of this heap is filled with zeroes using the ‘and dword’ operation (4 bytes zeroed out).</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/rtlalloc1.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/rtlalloc1.png" /> </a> </div> </div> <p>I couldn’t find any correct structure information on the web for this heap, so after a lot of trial and error, this is the structure of the FirstHeap (40/0x28 bytes) I created. This is also the heap that is returned by ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ at the end of the function, and thus we will call it ‘<code class="language-plaintext highlighter-rouge">VECTORED_HANDLER_ENTRY</code>’ structure. This structure contains LinkedLists as can be seen below. Also, note that the ‘<code class="language-plaintext highlighter-rouge">PVOID Refs</code>’ is nothing but the pointer to the SecondHeap (0xE3910) which we allocated after the FirstHeap allocation (0xE7AD0).</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-keyword" style="color: rgb(178, 148, 187);">typedef</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY *pNext; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY *pPrev; PVOID Refs; ULONG Padding0; PVECTORED_EXCEPTION_HANDLER pVectoredHandler; } VECTORED_HANDLER_ENTRY, *PVECTORED_HANDLER_ENTRY; </pre> <p>As can be seen below, the SecondHeap pointer (0xE3910) is moved to the 0x10th offset(PVOID Refs) of the FirstHeap (0xE7AD0).</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/dump1.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/dump1.png" /> </a> </div> </div> <p>The ‘<code class="language-plaintext highlighter-rouge">&lt;ntdll!RtlpGetCookieValue&gt;</code>’ stores a unique 4-byte Cookie (0xADD3C120). This is a random DWORD value when a process is created and is never the same. This value is used by the inline function ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlEncodePointer</code>’ to encode the VectoredHandler pointer. You will not see the ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlEncodePointer</code>’ function being called itself below, because it’s running inline. You can also see the value ‘one’ being moved to the SecondHeap (PVOID Refs -&gt; 0xE3910).</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/encode_pointer.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/encode_pointer.png" /> </a> </div> </div> <p>The ‘<code class="language-plaintext highlighter-rouge">xor rdx, rdi</code>’ instruction xor’s the VectoredHandler’s pointer (0x401670) with DWORD cookie (0xADD3C120). Once xor’d, it rotates the xor’d value for an ‘x’ number of times. This ‘x’ time is the last byte value of the cookie itself (0x20). This is the entire ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlEncodePointer</code>’ process. Once this ROR operation is complete, it is moved to the last 8 bytes of the FirstHeap (0xE7AD0).</p> <p>Next, ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrProtectMrData</code>’ is called to change the protection of ‘.mrdata’ section in ntdll.dll to ReadWrite. This is by default ReadOnly. ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrProtectMrData</code>’ uses ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ to change the permission from ReadOnly to ReadWrite. This is the part that I was trying to figure out. Apparently, in the older versions of Windows, this section didn’t exist. However, from Windows 8.1, the ‘.mrdata’ section was added to ntdll. This section stores mutable runtime data that needs to be protected during normal execution but occasionally modified. It holds structures like the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrSystemDllInitBlock</code>’, which contains data critical for managing system mitigations like Control Flow Guard (CFG). This section begins as writable but is later set to read-only to prevent tampering, with temporary unprotection allowed when needed during execution. This section also stores exception handler data (‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ struct) in a ReadOnly state, but we don’t know the offset for this yet, only Windows does. Since we are adding a new VectoredHandler, the pointer for this handler needs to be added to the VECTORED_HANDLER_ENTRY struct which we created above (FirstHeap (0xE7AD0)), and this data is then written to the ‘<code class="language-plaintext highlighter-rouge">.mrdata</code>-&gt;<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ section. This means I do not have any option to skip the usage of ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ which I initially thought I did from my experience with Windows 7.</p> <p>The ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrProtectMrData</code>’ first moves ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpMrdataLock</code>’ to RCX and calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAcquireSRWLockExclusive</code>’ to lock the section before changing permission. Next, it calls ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpChangeMrdataProtection</code>’ which calls ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ to change the permission of ‘.mrdata’ from ReadOnly to ReadWrite. The ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrProtectMrData</code>’ then calls ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlReleaseSRWLockExclusive</code>’ to release ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpMrdataLock</code>’. Once ReadWrite is enabled, we need to find the address for ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ (NOTE: THERES A CATCH). Ideally Windows knows the address for this which is different in different versions of ntdll. So, Windows can easily extract this information, but if we have to do it manually, then we will need to perform pattern-based hunting (More on this later). For now, Once this structure is extracted, The first pointer present in the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ is moved to RCX which is the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVehLock</code>’. Windows needs to perform a lock on this using ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAcquireSRWLockExclusive(LdrpVehLock)</code>’ without which writing to this section can cause race condition crashes due if reading and writing occurs at the same time. Once the writing is complete, ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlReleaseSRWLockExclusive</code>’ is called to release this lock.</p> <p>Once this list is extracted, a check is performed. If the second pointer in the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ is the same as self, then a PEB flag (CrossProcessFlags [PEB+0x50]) is enabled for VEH. This is done by calling the ‘<code class="language-plaintext highlighter-rouge">InterlockedBitTestAndSet</code>’ API call inline, which sets the flag value to 2.</p> <p>Another check is performed on the first parameter passed to ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’. If this value is non-zero, then the VectoredHandler needs to be called as the first handler, else last. To add a new handler to the start of the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’, we need to configure the ListEntries. A quick code would look like this where LdrpVectorHandlerList is the original list, and pNewVehEntry is our FirstHeap (0xE7AD0) buffer:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span>(FirstHandler) { <span class="hljs-comment" style="color: rgb(150, 152, 150);">//Add new node to the head of VEH</span> pNewVehEntry-&gt;pNext = LdrpVectorHandlerList-&gt;pFirstHandler; pNewVehEntry-&gt;pPrev = (PVECTORED_HANDLER_ENTRY)&amp;LdrpVectorHandlerList-&gt;pFirstHandler; LdrpVectorHandlerList-&gt;pFirstHandler-&gt;pPrev = pNewVehEntry; LdrpVectorHandlerList-&gt;pFirstHandler = pNewVehEntry; } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">else</span> { <span class="hljs-comment" style="color: rgb(150, 152, 150);">//Add new node to the end of VEH</span> pNewVehEntry-&gt;pNext = (PVECTORED_HANDLER_ENTRY)&amp;LdrpVectorHandlerList-&gt;pFirstHandler; pNewVehEntry-&gt;pPrev = LdrpVectorHandlerList-&gt;pLastHandler; LdrpVectorHandlerList-&gt;pLastHandler-&gt;pNext = pNewVehEntry; LdrpVectorHandlerList-&gt;pLastHandler = pNewVehEntry; }</pre> <p>In brief, if the argument provided to the ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ is non-zero, then:</p> <ol> <li>A new node (pNewVehEntry) is added at the beginning of the list. This ‘<code class="language-plaintext highlighter-rouge">pNewVehEntry</code>’ is the same FirstHeap (0xE7AD0) we allocated above.</li> <li>It points to the current first handler, and the current first handler’s ListEntry (pPrev) is updated to point back to the new node.</li> <li>The list’s ‘pFirstHandler’ is updated to the new node.</li> </ol> <p>If the argument provided is zero, then:</p> <ol> <li>The new node (pNewVehEntry) is added at the end of the list.</li> <li>It points to the start (‘pFirstHandler’ pointer) and the current last handler.</li> <li>The last handler’s ‘pNext’ is updated to the new node, and ‘pLastHandler’ is set to the new node.</li> </ol> <p>In our case, we supplied a non-zero value, so our handler should get added to the start of the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’. Here is a picture of before and after the list shuffling. You can check the FirstHeap (0xE7AD0) in RBX and bottom left dump, and original handler list in RAX and bottom right dump.</p> <p>Before Shuffling:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/before_list_entry.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/before_list_entry.png" /> </a> </div> </div> <ol> <li>Original first handler is 0x7FFC312D13F8 (RAX) which was initially extracted from the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’ (second offset of 8 bytes - this was garbage probably during initialization)</li> <li>Second handler is 0x7FFC312D13F8 (RDI). Both handlers are the same because there is no handler enabled yet.</li> <li>Original first handler list is moved to the start of the FirstHeap (0xE7AD0) [rbx]</li> <li>Second handler list is moved to the second offset of 8 bytes in the FirstHeap (0xE7AD0) list [rbx+8]</li> <li>FirstHeap (0xE7AD0) is moved to the second offset of 8 bytes in the first handler list [rax+8]</li> <li>FirstHeap (0xE7AD0) is moved to the first offset of 8 bytes in the first handler list [rax+8]</li> </ol> <p>After Shuffling:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/after_list_entry.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/after_list_entry.png" /> </a> </div> </div> <p>In short, our FirstHeap (0xE7AD0) structure now looks like this:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-keyword" style="color: rgb(178, 148, 187);">typedef</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY *pNext; <span class="hljs-comment" style="color: rgb(150, 152, 150);">// 0x7FFB5E3B13F8 - First Handler LinkedList</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">struct</span> _VECTORED_HANDLER_ENTRY *pPrev; <span class="hljs-comment" style="color: rgb(150, 152, 150);">// 0x7FFB5E3B13F8 - Next Handler LinkedList</span> PVOID Refs; <span class="hljs-comment" style="color: rgb(150, 152, 150);">// 0x0E3910 - Pointer to SecondHeap (contains 1)</span> ULONG Padding0; <span class="hljs-comment" style="color: rgb(150, 152, 150);">// Garbage - No idea what this is used for and irrelevant for now</span> PVECTORED_EXCEPTION_HANDLER pVectoredHandler; <span class="hljs-comment" style="color: rgb(150, 152, 150);">// Encoded Pointer for VectoredHandler</span> } VECTORED_HANDLER_ENTRY, *PVECTORED_HANDLER_ENTRY; </pre> <p>This dump on the top left in the image is the final buffer that gets returned by ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’ once it completes. Once all the writing is complete, ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlReleaseSRWLockExclusive</code>’ is called with ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVehLock</code>’ to release the VEH Lock. ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrProtectMrdata</code>’ is called again to reset the ReadOnly permission from ReadWrite for ‘.mrdata’, and a final ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrControlFlowGuardEnforced</code>’ is called again to perform CFG validation, before returning the FirstHeap (0xE7AD0) to the user.</p> <h2 id="part-iv-solution">Part IV: Solution?</h2> <h3 id="unhandled-exception">Unhandled Exception</h3> <p>So what was the solution to my problem? There was no solution. This is just another day as a Brute Ratel developer where I spend a ton of time reversing something, which may or may not be of much consequence. However, I did write an entire custom AddVEHHandler named ‘<a href="https://github.com/paranoidninja/Exception-Junction">RtlpAddVectoredExceptionHandler</a>’ which is the first custom handler ever written. And yes, this isn’t available on ‘unknowncheats.me’ or ‘reactos’ source code (atleast I couldn’t find it on web, or I am bad at google. lol). That was the first place I looked, only to find despair in return.</p> <p>However, there is one interesting part in the above code. In case of the original ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlAddVectoredExceptionHandler</code>’, Windows knows the address of the original first handler (‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’) from which it extracts the ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVehLock</code>’ and then updates it. If we are writing our own VEH handler code, then we will need to find this list. After a bit more digging, I found that ‘<code class="language-plaintext highlighter-rouge">ntdll!RtlRemoveVectoredExceptionHandler</code>’ stores this information and is easy to find using a pattern of ‘48 83 EC 20 44 8B ?? ?? 8D ?? ?? ?? ?? ?? 48 8B E9’.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2024-10-21-Exception-Junction/get_ldrpvehlist.png"> <img class="card-img-top img-fluid" src="/images/post_img/2024-10-21-Exception-Junction/get_ldrpvehlist.png" /> </a> </div> </div> <p>So, we can simply search the above pattern in the text section, extract the offset dynamically and add it to the current instruction pointer to get the address of ‘<code class="language-plaintext highlighter-rouge">ntdll!LdrpVectorHandlerList</code>’. The below code can be used to find the pattern and is a part of the above project.</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-function">PVOID <span class="hljs-title" style="color: rgb(129, 162, 190);">GetLdrpVectorHandlerList</span><span class="hljs-params" style="color: rgb(222, 147, 95);">()</span> </span>{ <span class="hljs-comment" style="color: rgb(150, 152, 150);">// Byte pattern for LdrpVectorHandlerList for windows 10 is: 48 83 EC 20 44 8B ? ? 8D ? ? ? ? ? 48 8B E9</span> <span class="hljs-comment" style="color: rgb(150, 152, 150);">// Pattern to search for: 0x4883EC20448BF24C8D254EEB0F00 (last 4 bytes are the offset)</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">const</span> BYTE pattern[] = { <span class="hljs-number" style="color: rgb(222, 147, 95);">0x48</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x83</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0xEC</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x20</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x44</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x8B</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0xF2</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x4C</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x8D</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">0x25</span> }; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">const</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">size_t</span> patternLength = <span class="hljs-keyword" style="color: rgb(178, 148, 187);">sizeof</span>(pattern); UINT_PTR hNtdll = findNtdll(); PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hNtdll; PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((BYTE*)hNtdll + dosHeader-&gt;e_lfanew); PIMAGE_SECTION_HEADER textSection = IMAGE_FIRST_SECTION(ntHeader); <span class="hljs-keyword" style="color: rgb(178, 148, 187);">for</span> (<span class="hljs-keyword" style="color: rgb(178, 148, 187);">int</span> i = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; i &lt; ntHeader-&gt;FileHeader.NumberOfSections; i++) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (<span class="hljs-built_in" style="color: rgb(222, 147, 95);">strncmp</span>((<span class="hljs-keyword" style="color: rgb(178, 148, 187);">const</span> <span class="hljs-keyword" style="color: rgb(178, 148, 187);">char</span>*)textSection-&gt;Name, <span class="hljs-string" style="color: rgb(181, 189, 104);">".text"</span>, <span class="hljs-number" style="color: rgb(222, 147, 95);">5</span>) == <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">break</span>; } textSection++; } BYTE* textSectionStart = (BYTE*)hNtdll + textSection-&gt;VirtualAddress; DWORD textSectionSize = textSection-&gt;Misc.VirtualSize; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">for</span> (DWORD i = <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>; i &lt; textSectionSize - patternLength; i++) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">if</span> (<span class="hljs-built_in" style="color: rgb(222, 147, 95);">memcmp</span>(textSectionStart + i, pattern, patternLength) == <span class="hljs-number" style="color: rgb(222, 147, 95);">0</span>) { <span class="hljs-keyword" style="color: rgb(178, 148, 187);">int32_t</span> offset = *(<span class="hljs-keyword" style="color: rgb(178, 148, 187);">int32_t</span>*)(textSectionStart + i + patternLength); BYTE* instruction_after_offset = textSectionStart + i + patternLength + <span class="hljs-number" style="color: rgb(222, 147, 95);">4</span>; BYTE* ldrpVehList = instruction_after_offset + offset; <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> ldrpVehList; } } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">return</span> <span class="hljs-literal" style="color: rgb(222, 147, 95);">NULL</span>; }</pre> <p>I’ve tested this against different ntdll versions and it worked, but it doesn’t work on Windows 7 as the pattern is different for that. It needs a different offset too, which I am sure you will find out if you are still reading this blog. So, that concludes this blog and an intro to the life of a ‘Real C2 Developer’ (pardon my shade XD).</p> <h2 id="social-responsibility-to-provide-detection-rule">Social Responsibility to Provide Detection Rule</h2> <p>For detection engineers, you can check ‘<code class="language-plaintext highlighter-rouge">ntdll!NtProtectVirtualMemory</code>’ call and the address of the ‘.mrdata’ section as a parameter which can be a big anomaly in itself, but for the ones who love yara rules, heres a thought.</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;">rule <span class="hljs-keyword" style="color: rgb(178, 148, 187);">HexBytePatternMatch</span> { meta: <span class="hljs-literal" style="color: rgb(222, 147, 95);">description</span> = <span class="hljs-string" style="color: rgb(181, 189, 104);">"Detect pattern in anything which is not ntdll"</span> author = <span class="hljs-string" style="color: rgb(181, 189, 104);">"Paranoid Ninja"</span> date = <span class="hljs-string" style="color: rgb(181, 189, 104);">"2024-10-12"</span> strings: <span class="hljs-variable" style="color: rgb(204, 102, 102);">$pattern</span> = { <span class="hljs-number" style="color: rgb(222, 147, 95);">48</span> <span class="hljs-number" style="color: rgb(222, 147, 95);">83</span> EC <span class="hljs-number" style="color: rgb(222, 147, 95);">20</span> <span class="hljs-number" style="color: rgb(222, 147, 95);">44</span> <span class="hljs-number" style="color: rgb(222, 147, 95);">8</span>B ?? ?? <span class="hljs-number" style="color: rgb(222, 147, 95);">8</span>D ?? ?? ?? ?? ?? <span class="hljs-number" style="color: rgb(222, 147, 95);">48</span> <span class="hljs-number" style="color: rgb(222, 147, 95);">8</span>B E9 } <span class="hljs-keyword" style="color: rgb(178, 148, 187);">condition</span>: <span class="hljs-variable" style="color: rgb(204, 102, 102);">$pattern</span> } </pre> <p>There are a few other ways on how one write to ‘.mrdata’ without being detected, but thats for some other day. Hope you guys enjoyed the blog, and theres much more in the arsenal of BRc4 2.1 Stay tuned and happy hacking :).</p>Chetan Nayak[email protected]This blog is in relation to some of the hurdles I’ve met while debugging and researching various new features for Brute Ratel. Before we get started, let me inform you that this blog is not for beginners. It requires some knowledge about Windows internals, exception handlers, and getting your hands dirty with a debugger, preferably x64dbg. And to add to that, there’s limited to near zero information on the web related to this topic, thus I spent the last 24 hours researching and writing this from scratch while being high on caffeine.Release v2.0 - Everything Everywhere All At Once2024-06-27T03:30:00+00:002024-06-27T03:30:00+00:00https://bruteratel.com/release/2024/06/27/Release-Metamorphosis<p>Brute Ratel v2.0 [codename Metamorphosis] is now available for download. This release introduces significant changes compared to previous versions, so it’s strongly recommended to review this blog, the private videos, and the documentation before using it. The Badger component has undergone extensive rewrites, featuring major updates in evasion tactics and new functionalities. The server has been optimized for speed and efficiency, with significant improvements to the licensing algorithm, ensuring each license is linked to a specific host to prevent misuse. However, the license can still be transfered from one host to another while deactivating the previous one. Additionally, several minor updates have been made to the Commander, which operators will notice during operation.</p> <p><strong>NOTE: This release is not compatible with any older releases. Neither the server, badger or the commander will be compatible. It is important to note the changes in this release before deploying them in production.</strong></p> <h2 id="strategic-changes">Strategic Changes</h2> <p>Before we dive into the various changes in this release, let us first understand why the changes were required. It is common knowledge that with the rising popularity of Brute Ratel, several organizations, large and small are inclined to purchase the product for red teams. As much as this is beneficial to Dark Vortex, this also brings in a few licensing problems. Several measures were taken to tackle this issue.</p> <h3 id="strengthening-licensing-enforcement-in-brute-ratel-v20">Strengthening Licensing Enforcement in Brute Ratel v2.0</h3> <p>A single Brute Ratel license permits use by only one user. According to the EULA, this license allows deployment on up to three servers: two public-facing servers and one for a local test-dev environment. This arrangement is based on my experience during red team assessments, where typically, a short haul server, a long haul server, and a test server configured to match the target infrastructure are sufficient for a single red team operation. If multiple red teams are being performed by an organization with multiple people, then multiple licenses would need to be purchased. However, it has been observed that some organizations were purchasing a single-user license and deploying it on a large scale, allowing multiple users to connect and operate on the server. To address this, the latest release of Brute Ratel strengthens the licensing algorithm to tie each license to the specific host on which it is activated. This is enforced through hardware checks on the host, ensuring that a license activated on one host cannot be copied to another. To activate Brute Ratel on a new host, the downloaded package must be transferred to the new host and activated there. This measure prevents the activation of the package on one host and its subsequent transfer to multiple other hosts. We recognize that this change poses a challenge for offline usage of the package. However, with this release, we are moving away from supporting offline activation due to potential misuse. Brute Ratel is designed for use by red teams, and not for offline penetration testing or use on HackTheBox machines, as has been observed with some customers. We remain committed to this intended use.</p> <h3 id="brute-ratel-usage-policy">Brute Ratel Usage Policy</h3> <p>Another strategic change in the sale of Brute Ratel is the decision to identify and sell the product only to organizations with experience in Red Team operations or a solid understanding of Windows internals. This change is due to instances where users lacked the necessary skills to effectively use Brute Ratel, expecting it to be a point-and-shoot tool, thereby compromising the integrity of the implants and ending up providing samples with zero opsec to security organisations. Brute Ratel IS NOT and WILL NEVER BE a point-and-shoot tool. Such tools are not suitable for Red Teams. While Brute Ratel includes official support for evasion and each release incorporates new evasion techniques tested with over 10 EDR software solutions, it is essential to understand that the operator is responsible for writing the loader, selecting the appropriate configurations, utilizing its malleability, configuring the stomped module, and stack configurations (described below), among other tasks. Brute Ratel offers the necessary options for configurations and builds shellcode based on these configurations, but safely executing the shellcode remains the operator’s responsibility. In light of this, we have decided to cease renewals and reject sales to organizations where operators lack knowledge or the willingness to learn about Red Teams (more emphasis on willingness to learn), and instead seek a tool that performs all tasks with a single click.</p> <h2 id="badger">Badger</h2> <h3 id="core-updates">Core Updates</h3> <p>The majority of the badger has been rewritten in this release to enhance evasion capabilities and update the architecture. Here are some of the key changes without delving too deeply into the internals:</p> <ol> <li>Introduced new sleep masking mechanisms that keep the badger’s stack-chain valid regardless of its sleep status.</li> <li>Sleep masking mechanisms can now be configured in a separate OpSec tab while creating a listener or payload profile.</li> <li>New options are available in the OpSec tab to configure the badger’s entry point, offset, core-thread execution method, and to build custom stack frames. If an operator does not configure any stack chain, a random legitimate looking stack is built depending on the process in which the badger is residing</li> <li>Significant updates to the SOCKS proxy have increased its speed and stability.</li> <li>All libraries can now be loaded with a custom stack chain defined by the operator.</li> </ol> <p>A more detailed version of the updates and their usage is available in the offline documentation.</p> <h3 id="other-improvements">Other Improvements:</h3> <ol> <li>[QOL] The ‘ls’ command now works without requiring a slash at the end of the directory.</li> <li>[QOL] The ‘ls’ command on an empty directory now returns a message indicating that the directory is empty, instead of returning no data.</li> <li>Added new commands: <strong>get winrm_config</strong>, <strong>set winrm_config</strong>, <strong>clear winrm_config</strong> and <strong>pivot_winrm</strong>, which use COM objects and WinRM for lateral movement.</li> <li>[QOL] The ‘rm’ command now enforces secure delete, making ‘rm -rf’ unnecessary.</li> <li>The default SOCKS 4a/5 timeout is now set to 3 minutes.</li> <li>Fixed the “Invalid architecture” bug in x86 BOFs.</li> <li>[QOL] Data in the listener input boxes are now trimmed for newlines and spaces.</li> <li>[QOL] Fixed the ‘timeloop’ command bug.</li> <li>[QOL] Improved the quality of screenshot captures.</li> </ol> <h2 id="ratel-server">Ratel Server</h2> <p>The core changes mentioned in the strategic policies above apply to the Ratel Server. Apart from these, several user requested features have been added.</p> <ol> <li>During a red team operation, having hundreds of badgers can slow down the Commander startup because it has to synchronize the badger tables with the server. This issue arises particularly when there are several inactive badgers that do not require interaction. Ideally, an operator can remove information about a dead badger from the Commander and Server using the context menu (right-click on the badger in the table). This action does not delete any command logs or entries; these are retained in the <strong>logs</strong> directory. However, problems occur if a removed badger checks in again. For example, if a badger goes offline because a phished system has gone to sleep, and the operator removes it from the Commander thinking it’s dead, it may come back when the system resumes from sleep. Since badgers use authenticated tokens, they cannot interact with the server if they have been removed, and they appear in the <strong>deauth.log</strong>. This release addresses this issue by enabling the import of badger profiles from the Commander in JSON format. An operator can create a badger profile or use one from the automatically generated <strong>autosave.profile</strong>, and import this JSON file into the server. If the server needs to be shut down during an engagement, this badger profile can also be used to import badgers along with a <strong>badger_counter</strong>. Additionally, if an operator fails to save the badger’s profile before removal, they can extract the badger ID and its token from old logs, and use random information for the rest of the JSON profile. This allows the badger to authenticate, and once authenticated, the operator can update the server with the correct information.</li> <li>Added badger_counter to autosave.profile. The counter increases with each newly added or connected badger.</li> <li>Updated service and DLL executable shellcodes to remove static detections.</li> <li>The Brute Ratel server binary now requires the operator to run the server as a root user.</li> <li>Removed redundant functions from various Go programs.</li> <li>Major updates to DNS Over HTTPS comms making it faster and more stable for heavy downloads.</li> </ol> <h2 id="commander">Commander</h2> <p>The latest release of Commander focuses primarily on quality of life (QoL) enhancements. Below are the key updates:</p> <ol> <li>New Listener and Payload Profile Options: <ul> <li>Added options to streamline the creation and editing of Listener and Payload profiles</li> </ul> </li> <li>Enhanced Badger Profile Management: <ul> <li>Introduced a button in the “Server Config” menu to import badger profiles for resurrected badgers</li> <li>This feature is useful for re-adding ‘removed badgers’ using their metadata. For example, if an operator removes a badger and it checks back in after a few days, it typically appears in the ‘deauth.log’ file. The data from ‘deauth.log’ or the initial access log can be used to retrieve the badger’s token and reintegrate it into the server.</li> </ul> </li> <li>Improved Command Help Output: <ul> <li>Updated the help output for commands that require special double quotes to properly escape single double quotes</li> </ul> </li> <li>Text Formatting Enhancements: <ul> <li>All data fields in Commander now have automatic space and newline truncation to prevent copy-paste errors</li> </ul> </li> </ol> <p>These updates aim to enhance the overall user experience by simplifying processes and reducing potential errors.</p>Chetan Nayak[email protected]Brute Ratel v2.0 [codename Metamorphosis] is now available for download. This release introduces significant changes compared to previous versions, so it’s strongly recommended to review this blog, the private videos, and the documentation before using it. The Badger component has undergone extensive rewrites, featuring major updates in evasion tactics and new functionalities. The server has been optimized for speed and efficiency, with significant improvements to the licensing algorithm, ensuring each license is linked to a specific host to prevent misuse. However, the license can still be transfered from one host to another while deactivating the previous one. Additionally, several minor updates have been made to the Commander, which operators will notice during operation.Release v1.9 - Eclipse2024-03-07T12:30:00+00:002024-03-07T12:30:00+00:00https://bruteratel.com/release/2024/03/07/Release-Eclipse<p>Brute Ratel v1.9 [codename Eclipse] is now available for download. This update includes enhancements in evasion techniques, anti-debugging measures, and new encryption keying methods for the core, along with an update to the licensing algorithm. Please note that the Ratel server, Commander, and previous versions of badgers are not compatible with v1.8 or older releases due to significant changes in the core architecture.</p> <p>One of the notable updates you’ll observe upon executing the Commander is the revamped interface for creating listener and payload profiler. This modification aims to enhance user readability and experience. Now, the two new options added to profiler and listener are the killdate and the keying strategy. Unlike the previous versions of bruteratel, wherein an operator had to configure the killdate post the execution of the badger, in this release its been modified to perform the validation of the date prior to the execution of the core. The default validity of the badger is set to 60 days from the generation of the badger, if nothing else has been configured here. You will also see the kill date for each badger in the server logs, when the badger is generated.</p> <p>The second major update is the keying strategy. Till version 1.8, the badger only supported a custom encryption method which is now known as the <strong>default</strong> method. This method hardcoded an operator provided encryption key in the badger, which was used to decrypt the core. This meant that it was possible to extract the key from the shellcode, and decrypt the core to build Yara rules. However, with this release there are several new encryption keying techniques in place. A detailed video on the keying mechanism is available for customers in the discord channel.</p> <p>These keying methods aim to make the extraction of the core harder than before, thus avoiding easier generation of yara rules. Ofcourse, a security solution can build detections on the external shellcode wrapper which executes the core, but the external shellcode wrapper does not trigger any kernel callback or etw which is responsible to initiate the scan for yara rules or opcode patterns, and the wrapper also gets erased once the core is executed, so that there is no trace of the wrapper.</p> <p>Other updates to this release include:</p> <ol> <li>The killdate feature is now activated prior to executing the implant. The default kill-date is set to two months from the date of badger generation if no configuration is specified.</li> <li>Implemented new keying and anti-debugging mechanisms to fortify the badger against reverse engineering.</li> <li>Improved DNS and DOH comms</li> <li>Updated LDAP Sentinel for <a href="https://github.com/fortalice/bofhound">bofhound</a> support. Output from LDAP Sentinel can be directly ingested into <code class="language-plaintext highlighter-rouge">bofhound</code> for Bloodhound analytics including support for security descriptors</li> <li>Added raw DNS badger generation in the PayloadProfiler</li> <li>Staging gets automatically disabled if listener is edited or restarted</li> <li>Updates for stack spoofing during sleep zero</li> <li>Updated raw DNS for windows7 and server2012 support</li> <li>Fixed HTML parsing bug in the UI where angular braces were escaped while printing the command in the badger’s terminal</li> <li>Added evasion for various yara rules</li> <li>Updated socks and rportfwd for speed and socket timeouts</li> </ol> <p>These are the only user-operatable changes in this release. Most of the major work was spent on rewriting certain sections of the badger for less footprint, the comm interface for the raw DNS and other internal changes for evasion. Additional information about these features are included in the documentation. The next release (v2.x) will introduce a completely redesigned and lightweight Brute Ratel (server, user interface and badger) from the ground up, marking the conclusion of major updates for the 1.x series. Stay tuned and Happy Hacking.:)</p>Chetan Nayak[email protected]Brute Ratel v1.9 [codename Eclipse] is now available for download. This update includes enhancements in evasion techniques, anti-debugging measures, and new encryption keying methods for the core, along with an update to the licensing algorithm. Please note that the Ratel server, Commander, and previous versions of badgers are not compatible with v1.8 or older releases due to significant changes in the core architecture.Release v1.8 - Mirage - Evading Every EDR On The Planet Part 22023-12-19T06:30:00+00:002023-12-19T06:30:00+00:00https://bruteratel.com/release/2023/12/19/Release-Mirage<p>Brute Ratel v1.8 [codename Mirage] is now available for download. This release provides a heavy update towards evasion and other feature requests by the community. Customers using v1.7 release should note that the Badgers of <strong>v1.7 will not support v1.8</strong>. Do not upgrade to this release if you are in an active engagement. Release notes have been disabled from here on out as we’ve noticed that it helps various security solutions to build detection capabilities on them. All blog updates/documentation will only contain minimalistic information on the internals starting from this release. Customers wanting further information can reach out to us on the dedicated email or discord support channel.</p> <h2 id="feature-additions">Feature Additions</h2> <p>This release contains major rewrites to various sections within the badger and server to optimize the code and make it more modular for OpSec. Some of the core parts that were updated were the unhooking mechanisms, shellcode unwrapper, data packing mechanism, comms and a few more. Apart from the heavy internal changes, there are three major striking features for this release:</p> <ul> <li>Full stack frame spoofing on sleep zero</li> <li>Exception handler unhooking</li> <li>Raw DNS Badger</li> </ul> <h3 id="stack-frame-spoofing-on-sleep-zero">Stack Frame Spoofing On Sleep Zero</h3> <p>The previous releases of Brute Ratel included stack frame spoofing, but only while the badger was sleeping. However, whenever an operator executes a command, the command was executed from the original stack frame. This meant security solutions could trace the stack via Vectored Exception Handling or via Kernel ETW and find the originating address of the shellcode. Once this is found, yara scans and pattern detection is executed on this memory region to hunt for known signatures. This could be avoided however with the help of module stomping which made the stack look clean, since stomped modules originate from disk unlike shellcodes which originate from memory region. With this release, irrespective of sleep zero, or socks proxy execution/remote port forwarding or any other command that an operator executes, the stack will be 100% legitimate. The traces wont return to the shellcode region. Previously, when you create a process and wait for its output, the stack could be easily seen as originating from memory, however thats no longer the case. All process output capture and every thread created by the badger uses a dynamically generated stack frame which is different and random everytime. This makes the indirect syscalls pretty much useless, as indirect syscalls only spoofed the return address.</p> <iframe width="400" height="250" src="https://www.youtube.com/embed/r05WNPkWO-U" title="Brute Ratel v1.8 - Mirage Sneak Peak - Custom Stack Frames" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe> <h3 id="exception-handling-unhooking">Exception Handling Unhooking</h3> <p>It was also observed that some EDRs used vectored as well as hardware breakpoint exceptions (SINGLE_STEP Exception) to capture several telemetry from a process. Although VEH unhooking was added way back when we reversed the Sentinel One EDR, we added the hardware breakpoint exception handling unhooking in this v1.8 release. More information on this was also posted on the twitter channel few days back:</p> <blockquote class="twitter-tweet"><p lang="en" dir="ltr">Got a question from a BruteRatel operator in the BRC4 discord channel as to what the newly implemented csxumd64_xxxxx.dll module in Crowdstrike does. So, heres my little explanation post reversing it.<br /><br />Crowdstrike implants 3 DLLs in their latest version - umppcxxxxx.dll,… <a href="https://t.co/K7ZA2NeQMo">pic.twitter.com/K7ZA2NeQMo</a></p>&mdash; Chetan Nayak (Brute Ratel C4 Author) (@NinjaParanoid) <a href="https://twitter.com/NinjaParanoid/status/1735682419569930362?ref_src=twsrc%5Etfw">December 15, 2023</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <h3 id="raw-dns-badger">Raw DNS Badger</h3> <p>This release extends the DNS over HTTPS functionality to use raw DNS badgers directly with the Brute Ratel server. This can be generated by right clicking the DOH/DNS listener and a selection dialog will pop-up to select the type of badger required. A single listener (DOH/DNS) provides both these functionality.</p> <h2 id="other-feature-improvements">Other Feature Improvements</h2> <ol> <li>Custom service executables can be added via ‘<em>register_psexec</em>’ profile. This can be enabled by adding the below json to the profile, or by uploading it to the Ratel server via Commander. Make note that the path should be reachable on the server. Commander does not upload the files. They should be present on the server side before this profile is uploaded. Both x86 and x64 service executables should be valid. This command does not support generic executables. Only service executables with ‘Service Main’ entrypoint are supported.</li> </ol> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;"><span class="hljs-string" style="color: rgb(181, 189, 104);">"register_psexec"</span>: { <span class="hljs-attr">"x64"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"/home/paranoidninja/Documents/BadgerSvc64.exe"</span>, <span class="hljs-attr">"x86"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"/home/paranoidninja/Documents/BadgerSvc86.exe"</span> } </pre> <ol> <li> <p>Added ‘<em>net_use add</em>’ and ‘<em>net_use del</em>’ commands for mounting remote shares. This command also support harvested credentials.</p> </li> <li>Until the previous release, <strong>Payload Profiler</strong> only supported stageless badgers. This release extends x64 and x86 Stages that can be created for alternate C2 servers from <strong>Payload Profilers</strong>.</li> <li>Ldap sentinel is now updated to return user security descriptors in base64 encoded format. This, when combined with <a href="https://github.com/fortalice/bofhound">BOFHound</a> from fortalice, provides proper support for Bloodhound queries.</li> <li>Listener window reloads if there is an error in starting the listener, instead of simply returning an error like before. This helps to avoid re-adding listener information on the Commander.</li> <li>Removed MAX_PATH limitation from ‘<em>ls</em>’ command.</li> <li>Download command supports files larger than 4GB.</li> <li>Some operators had an issue where the badger does not authenticate on expired SSL certificates. Added ssl certificate expiry checker when the ratel server starts, to notify the operator about the SSL certificate expiry date.</li> <li>Added API script ‘badgerRemove.py’ to use API to remove multiple badgers from the server programmatically</li> </ol> <p>Apart from these, we know that there were 2-3 more features that were requested on the discord channel which a lot of customers were waiting for. Those features will be added in the upcoming minor releases for v1.8. More information on these features are added in the documentation. There are several other backend changes which make this release more special in terms of OpSec. This release will however be the last release for this year. Stay tuned and Happy Hacking :)</p>Chetan Nayak[email protected]Brute Ratel v1.8 [codename Mirage] is now available for download. This release provides a heavy update towards evasion and other feature requests by the community. Customers using v1.7 release should note that the Badgers of v1.7 will not support v1.8. Do not upgrade to this release if you are in an active engagement. Release notes have been disabled from here on out as we’ve noticed that it helps various security solutions to build detection capabilities on them. All blog updates/documentation will only contain minimalistic information on the internals starting from this release. Customers wanting further information can reach out to us on the dedicated email or discord support channel.Release v1.7 - Pandemonium2023-07-27T06:30:00+00:002023-07-27T06:30:00+00:00https://bruteratel.com/release/2023/07/27/Release-Pandemonium<p>Brute Ratel v1.7 [codename Pandemonium] is now available for download. This release is an entire overhaul of the Badger, Ratel Server and Commander to provide support for Yara evasions and Apple Silicon. Customers using v1.6 release should note that the Badger, Ratel server and Commander of <strong>v1.6 will not support v1.7</strong>. Do not upgrade to this release if you are in an active engagement. Operators should read this blog or the release notes section to understand the changes before upgrading. A quick summary of changes can be found in the <a href="/release_notes/releases.txt">release notes</a>.</p> <h2 id="feature-additionsratel-serverbadger">Feature Additions:Ratel Server/Badger</h2> <h3 id="fallback-c2-strategy">Fallback C2 Strategy</h3> <p>During a red team engagement, an operator is usually limited to a few rotational hosts and one malleable profile. This allows the operator to switch back and forth between various hosts such as Azure, Fastly, Cloudfront or other fronted domains and redirectors. However, using the same profile to connect to multiple different domains can be suspicious over time. This release introduces ‘Fallback Strategy’ feature using which an operator can use multiple fallback malleable profiles for badger. Each fallback profile can contain another subset of fallback profiles enabling autonomous switching of profiles if one or more of your profiles/domains get blocked. Make note that each profile can still contain multiple rotational host for even more evasion. This can be mapped as follows:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/fallback_profile.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/fallback_profile.png" /> </a> </div> </div> <p>Each profile can contain an encrypted fallback profile metadata and the fallback profile can contain another profile and so on. The fallback profile is just another HTTP or DOH profile. This option can be configured either from Commander or via a C2 profile such as:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;">{ <span class="hljs-attr">"listeners"</span>: { <span class="hljs-attr">"primary-c2"</span>: { // ... your primary profile data <span class="hljs-attr">"fallback"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"fallback-c2"</span>, <span class="hljs-attr">"fallback_counter"</span>: <span class="hljs-number" style="color: rgb(222, 147, 95);">10</span> }, <span class="hljs-attr">"fallback-c2"</span>: { // .. fallback profile 1 data <span class="hljs-attr">"fallback"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"fallback-c2-2"</span>, <span class="hljs-attr">"fallback_counter"</span>: <span class="hljs-number" style="color: rgb(222, 147, 95);">5</span> } <span class="hljs-string" style="color: rgb(181, 189, 104);">"fallback-c2-2"</span>: { // .. fallback profile 2 data } } }</pre> <p>A detailed demonstration of the fallback strategy can be found in the below video:</p> <iframe width="500" height="300" src="https://www.youtube.com/embed/YVhP2kFvMfY" title="Brute Ratel v1.7 - C2 Fallback Strategy" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe> <h3 id="module-stomping-for-memexec-and-coffexec">Module Stomping for ‘memexec’ and ‘coffexec’</h3> <p>The v1.5 release of Brute Ratel introduced an Advanced Module Stomping technique for the badger wherein the stomped module’s PEB data is patched to make it look like a generically loaded module. This technique also restores the original buffer of the stomped DLL when the badger goes to sleep, so that tools like Pe-Sieve does not detect it as malicious by comparing the in-memory DLL buffer to disk. This release extends the module stomping technique to ‘memexec’ and BOF executions.</p> <p>Before you execute a BOF (coffexec) or a PE (memexec) in memory, you can configure a separate module to stomp using the ‘set module_stomp’ command. This module name can be fetched or cleared using ‘get module_stomp’ or ‘clear module_stomp’ command. Once a module has been configured, you can proceed to run your BOFs or PE, and they will be automatically mapped to the stomped module region. This region’s original DLL content is also restored once the BOF or the PE has completed execution. Make note that the module selected for stomping must have a ‘.text’ section larger than or equal to, the size of the PE or the BOF, else the stomping will fail with error ‘ERROR_ILLEGAL_DLL_RELOCATION’. ‘Coffexec’ and ‘memexec’ will also check if the module stomped is required by the PE or the BOF’s IAT. If it is, then the stomping again fails and prevents the badger from crashing as stomping a module required by your PE or object file is dangerous.</p> <iframe width="500" height="300" src="https://www.youtube.com/embed/L6gHusqUQW4" title="Brute Ratel v1.7 Post-Ex Evasions" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe> <h3 id="screen-recording">Screen Recording</h3> <p>This release brings in a screen recording feature which becomes extremely important if you often perform Red Team engagements in a banking environment. Recently during one of our red team engagements for a bank, one of the objectives was to gain access to the SWIFT server and perform a few activities on the SWIFT banking software. Due to limited knowledge and little internet information on how a SWIFT server works, the best idea was to monitor the day-to-day activities of a SWIFT operator and understand the software. This is why the screen recording feature was added. The ‘record_screen’ command can take arguments as quality (low/medium/high) and number of minutes to record the target’s screen. The recording can also be stopped before the timer completes by using the ‘stop_task’ command.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/recording.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/recording.png" /> </a> </div> </div> <h3 id="full-unicode-support">Full Unicode Support</h3> <p>This release provides full support for Unicode characters, both on the badger as well as on the Commander’s end. Unicode characters involving russian, latin, chinese and others are all supported by the below commands:</p> <ul> <li>cd</li> <li>ls</li> <li>File Explorer (GUI)</li> <li>Ldap Sentinel (GUI)</li> <li>runas</li> <li>impersonate</li> <li>vault_remove</li> <li>make_token</li> <li>cp</li> <li>mv</li> <li>mkdir</li> <li>rmdir</li> <li>rm</li> <li>download</li> <li>preview</li> <li>fileinfo</li> <li>acl</li> </ul> <p>Due to changes to these commands, some commands need escaped slashes unlike before. These commands are ‘cp’, ‘mv’ and ‘download’. More information can be found in the ‘help’ section within the badger’s terminal for each command.</p> <h3 id="memexec-profiler">Memexec Profiler</h3> <p>This release also extends the ability to add ‘memexec’ as a built-in command via ‘register_exe’ profile. A sample command profile for ‘register_exe’ would look like:</p> <pre class="hljs" style="display: block; overflow-x: auto; background: rgb(29, 31, 33); color: rgb(197, 200, 198); padding: 0.5em;">{ <span class="hljs-attr">"register_exe"</span>: { <span class="hljs-attr">"handles"</span>: { <span class="hljs-attr">"arch"</span> : <span class="hljs-string" style="color: rgb(181, 189, 104);">"x64"</span>, <span class="hljs-attr">"file_path"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"server_confs/sample_profile_pe/handle64.exe"</span>, <span class="hljs-attr">"description"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"Lists all handles from all processes. Uses sysinternal's handle64.exe executable to run in memory"</span>, <span class="hljs-attr">"artifact"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"WINAPI"</span>, <span class="hljs-attr">"mainArgs"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"NA"</span>, <span class="hljs-attr">"optionalArg"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"NA"</span>, <span class="hljs-attr">"example"</span>: <span class="hljs-string" style="color: rgb(181, 189, 104);">"handles"</span>, <span class="hljs-attr">"minimumArgCount"</span>: <span class="hljs-number" style="color: rgb(222, 147, 95);">1</span> } } }</pre> <p>The set, clear and get commands are now clubbed under a singular title for ease of access. Previously, the configuration commands were separate, for eg.: ‘set_child’ or ‘set_parent’ etc. This led to an increased number of commands just to configure various aspects of the badger. With this release, the set, clear and get commands are clubbed into each of their respective titles.</p> <h2 id="feature-additionscommander">Feature Additions:Commander</h2> <p>This release adds official support for Commander for Apple Silicon’s ARM architecture and Windows 11. Each Commander (linux/mac and windows) is built individually to support their respective operating system such as path auto completion, fonts and other user interface widgets. Linux uses the ‘Monospace’ font by default, Windows 10/11 uses ‘Courier New’ and MacOS uses ‘Monaco’ for the entire GUI. However, this release also provides the ability for the operator to change the theme and font dynamically at runtime as well as store them in a local json file, so that the configuration of the Commander can be auto-read during startup. Operators can also write stylesheets for themes and preview them dynamically by Selecting the ‘Commander’ menu and then selecting the ‘Settings’ option. This means there is no need to start and stop the commander to apply themes anymore.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/settings.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/settings.png" /> </a> </div> </div> <p>To ease the life of an operator, the option to upload profiles for Listener, Commands, Autoruns and Clickscripts are now added to Commander. Operators can select the “Load json profile” button in the respective ‘Add HTTP Listener’ or ‘Add DOH Listener’ dialog, or select the Upload option in the ‘C4 Profilers’ section.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/profiler.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/profiler.png" /> </a> </div> </div> <p>Various samples for each of these profiles are provided in the profiles directory within the brute ratel package. A quick example to upload the profiles can be found in the video below:</p> <iframe width="500" height="300" src="https://www.youtube.com/embed/2xPtBxoXszw" title="Brute Ratel v1.7 - Dynamic Profilers" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe> <p>A notification smiley has been added to the top right side of Commander to display the online and offline status. Depending upon how your badger connects, this is updated frequently. If your badger is configured to sleep zero, it will show up as (o_o), for terminated connection with Server, it shows up as (X_X), else it will show up as idle.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/status_1.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/status_1.png" /> </a> </div> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/status_2.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/status_2.png" /> </a> </div> <div class="card"> <a href="/images/post_img/2023-07-27-Release-Pandemonium/status_3.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-07-27-Release-Pandemonium/status_3.png" /> </a> </div> </div> <p>Some other changes include:</p> <ol> <li>The ‘clear’ command (to clear the badger’s command queue) is renamed to ‘clearq’ as ‘clear’ command is used to clear various other command configurations for the badger</li> <li>The Pivot, Mitre and Team Graphs are now directly exported to html for reporting purposes instead of rendering them within Commander</li> <li>The Bulk query runs as a standalone dialogue instead of being docked within Commander</li> <li>Added option to wordwrap badger’s terminal</li> <li>Updates to light and shady theme</li> <li>Removed the word ‘auto-‘ from listener and auto profile generation</li> </ol> <p>Various other changes were made to Brute Ratel, to make it more evasive and smoother to operate across various operating systems. Users will observe those changes as they migrate from v1.6 to v1.7 Commander. More detailed information on the changes are mentioned in the release notes. The upcoming weeks will also see a few more blogs on the roadmap of Brute Ratel and how it is going to provide users with a heavy customization option when dealing with various EDRs ;)</p> <p>Apart from these updates, Dark Vortex will be conducting free Seminars every month on the usage of Brute Ratel which should help customers understand the core of the product and why it is more successful and evasive than any other C2s in the current market. The current BRc4 Seminar is scheduled for 31st July 7 AM UK (GMT +1:00). Stay tuned and Happy Hacking :)</p>Chetan Nayak[email protected]Brute Ratel v1.7 [codename Pandemonium] is now available for download. This release is an entire overhaul of the Badger, Ratel Server and Commander to provide support for Yara evasions and Apple Silicon. Customers using v1.6 release should note that the Badger, Ratel server and Commander of v1.6 will not support v1.7. Do not upgrade to this release if you are in an active engagement. Operators should read this blog or the release notes section to understand the changes before upgrading. A quick summary of changes can be found in the release notes.Release v1.6 - Reboot2023-05-30T06:30:00+00:002023-05-30T06:30:00+00:00https://bruteratel.com/release/2023/05/30/Release-Reboot<p>Brute Ratel v1.6 codename Reboot is now available for download. This release brings in several updates to existing evasion techniques, support for Windows Commander, Hi-DPI scaling and various heavy user experience updates (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the <a href="/release_notes/releases.txt">release notes</a>.</p> <h2 id="feature-additionsratel-serverbadger">Feature Additions:Ratel Server/Badger</h2> <h3 id="ldap-sentinel">LDAP Sentinel</h3> <p>This release brings in support for sleep and jitter for LDAP Sentinel with the ‘sentinel_sleep’ command. Using this, operators can provide an interval between every single LDAP request to the Domain Controller. Unlike previous releases, this version of LDAP Sentinel supports SASL authentication with a fallback mechanism to the default kerberos authentication. The SASL authentication consists of encrypted messages inside the LDAP “bind” requests and responses. The “bind” request contains the distinguished name of the directory object that Badger wishes to authenticate as either with an impersonated token or directly. This feature was added to support forced Certificate SASL authentication within some environments. Interestingly, this also provides better evasion against network based IDS which build detections against known LDAP queries from unencrypted data or by tracking multiple LDAP queries originating from one source and then tagging it as an anomaly. Due to the encrypted nature of the SASL authentication, it becomes difficult for various detection systems which do not handle SASL. Apart from these changes, LDAP Sentinel also supports attribute filtering. An operator can now provide multiple attribute filters within LDAP filters to limit search output to requested attributes. The below example shows attribute filters (name, distinguishedName, lastlogon and objectSid) added to the LDAP query to search user objects.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/ldap_sleep.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/ldap_sleep.png" /> </a> </div> </div> <p>This feature when coupled with <a href="https://github.com/fortalice/bofhound">BofHound</a> makes it easier to build custom bloodhound compatible json files.</p> <h3 id="acl-enumeration">ACL Enumeration</h3> <p>The <a href="https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptordacl">GetSecurityDescriptorDacl</a> API provides a way to enumerate a Discretionary Access Control List for an object. Brute Ratel uses this API with the newly added ‘acl’ command to enumerate permissions of a file or folder similar to the ‘cacls.exe’ executable.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/acl_enum.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/acl_enum.png" /> </a> </div> </div> <h3 id="environment-variables">Environment Variables</h3> <p>During a red team engagement, a network service process or an IIS Server process, when exploited, might not have proper environment variables. In one of the previous releases, we introduced the ‘getenv’ command to enumerate the process environment variables. This release extends this functionality to add custom environment variables for the current process using the ‘setenv’ command.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/setenv.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/setenv.png" /> </a> </div> </div> <h3 id="curl-request">Curl Request</h3> <p>A miniature ‘curl’ functionality was added to this release to perform http/https requests to a given site and url. This command can send a GET request to HTTP/S server on a given port and URI and receive html output in raw format. This can be extremely handy to search and enumerate internal web applications without the need to start Socks proxy every now and then, or to check if your backup C2 channel is reachable from within the organizational environment.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/curl_request.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/curl_request.png" /> </a> </div> </div> <h3 id="secure-deletion-of-files">Secure Deletion Of Files</h3> <p>Another anti-forensic feature that was requested by the community was to securely delete the files so that it cannot be extracted by the blue team using EnCase and Autopsy. This is an optional feature added to the ‘rm’ command which accepts the ‘rf’ argument. When this command is executed, the requested file is overwritten with garbage and zeroes out every bit multiple times before deletion. This makes recovery of files extremely difficult. Make note that large files will take more time for secure deletion, as more bytes are written to disk.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/secure_delete.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/secure_delete.png" /> </a> </div> </div> <h3 id="dll-load-telemtry-capture-in-userland">DLL Load Telemtry Capture In Userland</h3> <p>An interesting feature observed with an EDR was tracking of DLL loads in userland. Most EDRs monitor DLL loads in the kernel via the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetloadimagenotifyroutineex">PsSetLoadImageNotifyRoutineEx</a> callback. However, for unknown reasons it was observed that some EDRs capture this information in the userland by hooking LoadLibrary events. One theory behind this would be that kernel callbacks provide a limited amount of information such as the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_image_info">IMAGE_INFO</a> structure. Userland Hooking can provide an EDR more leverage to quickly perform remediations without having to use Kernel CPU time. This release of BRc4 disables userland DLL notifications by default.</p> <h3 id="everyone-acl-for-smb-badger">Everyone ACL For SMB Badger</h3> <p>Another community request was to modify the way SMB payload handled authentication from remote systems. Usually it’s a good idea to allow only authenticated users to connect to the named pipe. However, this isn’t feasible when the SMB shellcode is executed via IIS server exploitation which you do not have tokens or passwords of. Thus, the only way to connect to the named pipe is by changing the DACL of the named pipe. This release disables SMB authentication on the named pipes and allows all users to connect to the Badger’s named pipe.</p> <h3 id="updates-to-the-core">Updates To The Core</h3> <p>Various changes were made to the Ratel Server and Badger architecture for evasion, stability and usability. The following list includes a few of the major changes:</p> <ul> <li>The staged shellcode supports malleability data for response data (request malleability already existed). Both staged and stageless payloads transfer data in encrypted context using a custom encryption algorithm. However, having encrypted data over the network increases the entropy of the network data. It was observed that Crowdstrike, Defender ATP and a few other EDRs strip the SSL encrypted data by installing root user certificates and checking the network data entropy. Various changes were made to how the data is being sent over the network, in order to lower the entropy. The previous releases of Badger had an entropy of the value of 7.4/10 which was on a higher side. This release brings down the network entropy to 4/10 for both staged and stageless payloads, which can be lowered further by adding malleable data.</li> <li>There was a major race condition when module stomping was used alongside sleep obfuscation. This is now fixed and helps to avoid detections from Elastic EDR and FortiEDR which check the RX region of selected API calls for anomalies.</li> <li>Sleep interval for file downloads now change with the sleeping schedule configured for the main thread.</li> <li>Updated entire DNS comms with a more robust backend server rewritten from scratch for enhanced logging, fast and stable communication.</li> </ul> <h2 id="feature-additionscommander">Feature Additions:Commander</h2> <p>One of the most requested features of Commander was support for custom themes. What better way to support this, if not via stylesheets. Operators can now write custom stylesheets and change every aspect of the Commander, adding different fonts, colors and font-size for every widget and dialog box. The default theme is Dark, however ‘light’ and ‘shady’ themed stylesheets are provided in the package. Operators can use them as a reference to write their own stylesheet. The generated stylesheet file can be passed as a command line argument to the shellscript ‘commander-runme.sh’.</p> <h3 id="dark-theme-with-solarized-terminal-default">Dark Theme With Solarized Terminal (default)</h3> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/brc4_dark.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/brc4_dark.png" /> </a> </div> </div> <h3 id="shady-theme">Shady Theme</h3> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/brc4_shady.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/brc4_shady.png" /> </a> </div> </div> <h3 id="light-theme">Light Theme</h3> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-05-30-Release-Reboot/brc4_light.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-05-30-Release-Reboot/brc4_light.png" /> </a> </div> </div> <p>Starting from this release, official support is provided for Commander to be run on Windows 10. The package includes the ‘commander-runme.bat’ file which loads up all dependencies and executes the main Commander executable. Make note that Pivot and MITRE graphs are not supported in windows due to some limitations of the chromium library used to render graphs in linux. Entire backend of the Commander was rewritten for a better user experience. Some of the highlights include:</p> <ul> <li>Support for Hi-DPI Scaling on Windows and Linux</li> <li>Added ‘back’ and ‘forward’ buttons to search backward and forward in the Badger’s terminal</li> <li>Commander’s ‘disconnected’ dialog is replaced by a simplified green circle at the extreme top right location (check the above Commander figure). This area shows whether Commander is connected (green circle) or disconnected (in red).</li> <li>Added info-pane to Badger’s Terminal which shows additional info of the Badger (check the Badger’s terminal in the above figure) <ul> <li>User name</li> <li>Current working directory</li> <li>Last sleep time and jitter</li> <li>Active sleep obfuscation</li> <li>Active socks proxy</li> <li>Active rportfwd</li> </ul> </li> <li>Removing badgers from the Commander perma-deletes dead badgers. DO NOT REMOVE active badgers. Deleting badgers will delete their authentication information from the server making them unable to authenticate anymore if they check in later. Logs will not be deleted.</li> <li>Commander shows active Rportfwds and TCP listeners in the “Server” drop down menu</li> <li>The download tab supports perma-delete of downloaded files. Files can now be downloaded or deleted using multi-selection.</li> </ul> <p>There were various other improvements made to the Ratel Server’s backend, Badger commands and Commander, some of which are listed in the release notes. Various other evasion updates have also been added which are not added to the release notes to keep it away from the hands of defenders. There are a few more post-exploitation feature releases scheduled for the upcoming month, which were supposed to be added in this release (as mentioned in the discord channel), but were not added due to their stability issues. However, they will be pushed as a part of minor release once stable. Users are requested to update to this release as it provides several QOL over the previous release. Stay tuned and Happy Hacking :)</p>Chetan Nayak[email protected]Brute Ratel v1.6 codename Reboot is now available for download. This release brings in several updates to existing evasion techniques, support for Windows Commander, Hi-DPI scaling and various heavy user experience updates (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the release notes.Release v1.5 (Nightmare) - Ghosts From The Past2023-03-19T06:30:00+00:002023-03-19T06:30:00+00:00https://bruteratel.com/release/2023/03/19/Release-Nightmare<p>Brute Ratel v1.5 codename Nightmare is now available for download. This release brings in new evasion techniques and user experience updates (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the <a href="/release_notes/releases.txt">release notes</a>. This release also brings several changes to the licensing server which now provides support for backward compatibility. More on this at the end of the blog.</p> <h2 id="feature-additions">Feature Additions</h2> <h3 id="revamping-module-stomping">Revamping Module Stomping</h3> <p>The highlight of this release is module stomping. <em>Nightmare</em>, the name of the release is what I went through, in order to get this feature working. Module stomping, in itself is not a new feature. This was introduced several years back in Cobaltstrike, however the technique we are going to discuss today is very different from anything publicly available. Module Stomping is pretty handy as it overwrites the ‘.text’ section of a DLL. This means all calls originating from our shellcode/reflective DLL will be backed by a file on disk. Thus, there is no need for call stack spoofing and it provides easy evasion from ETW and similar detections. However one has to be careful on how it is implemented, else it becomes evidently <a href="https://github.com/slaeryan/DetectCobaltStomp">easy to detect, as is the case of Cobaltstrike</a>. Lets split our technique into 3 sections and walkthrough through it in detail. If you dont want to read through this below, you can go through <a href="https://youtu.be/nPmcFKSHyvg">this youtube video</a> here wherein I explain the same things below.</p> <h4 id="1-the-curious-case-of-loadlibraryex">1. The Curious Case of LoadLibraryEx</h4> <p>Cobaltstrike and most other module stomping techniques use LoadLibraryEx to load a DLL into memory. LoadLibraryEx does not call the entrypoint (Dllmain) of the loaded DLL and it does not resolve IAT. If we use <em>LoadLibrary</em> instead of <em>LoadLibraryEx</em>, the windows loader calls Dllmain and links our DLL into PEB. This is not suitable for module stomping as our stomped DLL might load other DLLs, which can start some threads. Thus if we overwrite this region with our shellcode/rdll, we might end up crashing as there might be threads which are already running via DllMain. Thus <em>LoadLibraryEx</em> ends up being the perfect choice of API for module stomping, which takes in a third parameter ‘<em>DONT_RESOLVE_DLL_REFERENCES</em>’. This means our DLL will be loaded from disk and mapped into memory, but the windows loader wont call its entrypoint. Another important thing to note here is that since the loader is not calling the entrypoint, it will change some Flags in the _LDR_DATA_TABLE_ENTRY which is critical for module stomping detection. For the defenders, here are a list of things to monitor:</p> <ul> <li>Entrypoint of the stomped DLL is null. This means the DLL does not have an entrypoint. Thus whenever there are “PROCESS ATTACH/THREAD ATTACH” events within the process, this Dllmain will not be called</li> <li>ImageDLL flag is marked as false. This means DLL was loaded as an Exe and not DLL</li> <li>LoadNotificationsSent flag is marked as false. This means the DLL’s load notification was not sent</li> <li>ProcessStaticImport will be false. This means the import of the DLL is not processed</li> </ul> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-03-07-Release-Nightmare/non_spoofed.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-03-07-Release-Nightmare/non_spoofed.png" /> </a> </div> </div> <p>The above anomalies are usually enough to detect Cobaltstrike or other module stomping techniques. Another easy way to detect module stomping is to check the ‘.text’ region of the DLL on disk and the one in the memory of the process.</p> <h4 id="2-full-peb-linking-and-entrypoint-patching">2. Full PEB Linking and Entrypoint Patching</h4> <p>Linking PEB flags are not as hard as it is to spoof the entrypoint. In case of LoadLibraryEx, we can see in the image above, that it is set to null. The reason being that whenever any new DLL gets loaded, the loader walks this PEB and calls the Dllmain of all the DLLs in memory. This is the design implementation of microsoft wherein the Dllmain accepts 3 arguments. The second argument is provided by the loader to the Dllmain whenever there are PROCESS ATTACH, DLL THREAD ATTACH, DLL THREAD DETACH AND DLL PROCESS DETACH events. Thus, if an entrypoint added to the PEB is invalid, our process will crash. If we keep it to null, the windows loader ignores it. This means we have to make sure the correct entrypoint of the DLL is added added here.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-03-07-Release-Nightmare/dllattached.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-03-07-Release-Nightmare/dllattached.png" /> </a> </div> </div> <p>NOTE: Image is from <a href="https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain">microsoft website</a></p> <h4 id="3-control-flow-guard">3. Control Flow Guard</h4> <p>If you have succesfully linked the PEB and avoided generic detections like that of Cobaltstrike, we have another problem at hand. In the Windows 8.1 Update KB3000850, microsoft introduced <a href="https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard">Control Flow Guard</a> which blocked any indirect calls originating from an invalid function start address. This means all indirect calls should be called from a valid start address and not from the middle of any code from any ‘.text’ region. This information about the valid function locations are stored as the <em>Characteristics</em> of each section in the PE which can be read using a CFF explorer by doing bitflag checks. However this is a bit trickier than expected. Ideally, if we just copy our code to the entrypoint of a stomped DLL, it should work because entrypoint is a valid start of a function. Lets take an example of staged metasploit shellcode which is of 512 bytes. If our entrypoint code (in DLL) is of, say only 1000 bytes, and our shellcode that we copied is of 512 bytes (metasploit), our staged code will simply allocate new memory, copy reflective DLLs to the newly allocated region and execute them. This is not operationally safe, because we are indirectly allocating a new region. Thus most POCs you see with metasploit will work (as POC only), but does not do any benefit in terms of evasion for the final stage. Because unlike POCs, to perform proper evasion, we have to make sure our full PE code is backed by a valid <code class="language-plaintext highlighter-rouge">.text</code> section on disk. This means we cannot use staged code, and our reflective DLL or second stage should be within the .text region and it should start from a valid call target to avoid CFG.</p> <p>All C2s use some sort of PE or reflective DLL for stageless payloads and these are usually more than 150-200kb as they might contain several post-exploitation code unlike staged code. So what happens if our <code class="language-plaintext highlighter-rouge">.text</code> region is say 300kb, entrypoint code is of, say only 1000 bytes, and our shellcode that we copied is of 200 kb reflective DLL? This means we end up overwriting another function in the ‘.text’ region. Now if we perform any type of threaded calls from this region, especially if the process is a CFG-enabled process (which most of the windows processes are), then we will end up calling <code class="language-plaintext highlighter-rouge">ntdll!LdrpDispatchUserCallTarget</code> which will check the indirect call location and the bitflags enabled for that location. The <code class="language-plaintext highlighter-rouge">ntdll!LdrpDispatchUserCallTarget</code> is an internal function of ntdll.dll (notice the p in Ldrp) which takes an argument in the RCX register. This argument is the address region which needs to be vetted for invalid call targets. If the region from where the call is originating invalid, <code class="language-plaintext highlighter-rouge">ntdll!LdrpDispatchUserCallTarget</code> calls <em>RtlFailFast2</em> with a <em>STATUS_STACK_BUFFER_OVERRUN</em> exception which kills our process instantly. More details on this error can be found in this <a href="https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655">blog</a>. This means, if we want to evade CFG, we have to disable CFG on our stomped DLL’s executable region. A detailed workflow on CFG can be found in <a href="https://documents.trendmicro.com/assets/wp/exploring-control-flow-guard-in-windows10.pdf">this blog written by Trend Micro</a>. Below is the callstack for a thread which called <em>LdrpDispatchUserCallTarget</em>.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-03-07-Release-Nightmare/ldrpcallstack.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-03-07-Release-Nightmare/ldrpcallstack.png" /> </a> </div> </div> <h3 id="advanced-module-stomping-evasion">Advanced Module Stomping Evasion</h3> <p>Badger overcomes all the above anomalies in two ways:</p> <ol> <li>Badger deletes all of its region from the memory of the process while sleeping and restores the original DLL’s buffer till the sleep is complete. This is done alongside stack spoofing, stack encryption and heap encryption to avoid all traces of the badger and it’s data while sleeping. Thus if anyone scans the stomped module to perform a comparison of on-disk and in-memory regions while the badger is sleeping, it would look the same. When the badger is not sleeping, all of the evasions still work except the .text region of the DLL contains the badger’s .text region.</li> <li>The PEB LDR module uses a custom hook to reflect the necessary changes to avoid detections for entrypoint and DLL Flags which can also be seen in the above youtube video.</li> </ol> <p>For people interested in using their own custom module stomping technique, I have added commands <code class="language-plaintext highlighter-rouge">cfg_disable</code> and <code class="language-plaintext highlighter-rouge">cfg_enable</code> to disable or enable Control Flow Guard. This is not required for the badger, but if you want to create a process and perform module stomping injections with your own post exploitation toolkit, then CFG can be enabled to disabled using these commands.</p> <p>Make note that even with all these evasions, its an operator who has to be careful with what module they want to stomp. Overwriting sections of bad DLLs can lead to a process crash, especially if the DLL you stomped is being utilized by some other module/code in your process. The module stomping feature can be enabled via Payload Profiles or during Listener creation. Make note that module stomping is disabled for DLLs generated by the Commander. Reason being if a DLL loads a module and calls its DllMain, this Dllmain (now badger) will call LoadLibrary to load other DLLs. But since this Dllmain will be under loader lock, you cannot load other DLLs. Thus module stomping will not work with DLLs or DLL sideloads. Staging also supports module stomping. This means you can stomp a stage yourself, and let the stage stomp your stageless code into another stomped module.</p> <h3 id="process-injection-via-remote-procedure-call">Process Injection via Remote Procedure Call</h3> <p>This release brings in a new undocumented injection technique via remote procedure call. This can be enabled on the fly for remote process injections via <em>set_threadex</em> command similar to other process injection techniques. The ID for this technique is 12. Make note that your target process needs to be in an alertable state to perform this injection.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-03-07-Release-Nightmare/rpc.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-03-07-Release-Nightmare/rpc.png" /> </a> </div> </div> <h3 id="pass-the-hash">Pass The Hash</h3> <p>This feature addition is a result of several user requests for a built-in Pass The Hash functionality. You can choose to spawn a new process from an ntlm hash or simply impersonate a token from it. This pth is an improvised version of Mimikatz’s PTH functionality with more built-in OpSec.</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-03-07-Release-Nightmare/pth.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-03-07-Release-Nightmare/pth.png" /> </a> </div> </div> <p>Apart from the major updates above, several changes were made to the shellcode, badger’s core and Commander. Below is a quick list of those changes</p> <ul> <li>Webhook forwards the command name alongside the output received from the badger. This can be use to actively track various command outputs via API without having to use ‘greedy’ parsing when data is forwarded to logstash or elasticsearch</li> <li>Replaced RtlRegWait function for proxy calling of Windows API and NTAPI to a new undocumented function</li> <li>Improved http header parsing for the badger. Commas can be used in the http headers now. Applies for Stage Zero too</li> <li>Improved post request/response parsing for badger. Any values can be used in the http requests/responses now</li> <li>Updated <em>memexec</em> hooks for ETW evasion</li> <li>Added module stomping option to listeners and payload profilers in Commander</li> <li>Added color option to highlight the double clicked folder in File Explorer</li> <li>Listing files in File Explorer now also displays files in the terminal</li> <li>Improved Commander terminal for stop and go scrolling. Users can scroll to the top without the scroll being autoreset to the latest output when a new output is added by the server</li> <li>Process Managers and File Explorers UI for every badger are now locked to a single instance</li> <li>Fixed HTTP Edit Listener option to overwrite existing malleable requests instead of showing the same one when loaded</li> </ul> <p>Brute Ratel’s download page now also supports downloading an older version of the package. You can select the latest package or one release (main) behind the current release. This previous release will be the last major release of the previous version. Since v1.5 is now released, v1.4.5 will stay active as the previous package till v1.6 is released. Thus, if v1.5.1 or v1.5.2 gets released, the older version will still stay v1.4.5 as these releases are minor releases of v1.5.</p> <p>Several other changes were made to the backend to optimize how the server handles the badger data and the shellcode generation. This release more or less likely, covers up the much needed post-exploitation techniques within the badger. There are a few more scheduled releases for the upcoming month which introduce some brand new ways of evasion for BRc4. Stay tuned and Happy Hacking :)</p>Chetan Nayak[email protected]Brute Ratel v1.5 codename Nightmare is now available for download. This release brings in new evasion techniques and user experience updates (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the release notes. This release also brings several changes to the licensing server which now provides support for backward compatibility. More on this at the end of the blog.Release v1.4 (Blitzkrieg) - Reflection In a Nut Shell2023-01-04T06:30:00+00:002023-01-04T06:30:00+00:00https://bruteratel.com/release/2023/01/04/Release-Blitzkrieg<p>Brute Ratel v1.4 codename Blitzkrieg is now available for download. This release brings in a few new features, updates to EtwTI evasion techniques, and user experience (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the <a href="/release_notes/releases.txt">release notes</a>.</p> <h3 id="pricing-and-support">Pricing and Support</h3> <p>Before we dive into the feature updates, an important announcement I would like to highlight is the change in pricing. The year 2022 was a great year for Dark Vortex as we crossed the 600 customer mark. Brute Ratel has always followed the strategy to keep the cost at minimum while still providing utmost stability and cutting-edge research to avoid detection against EDR and Antivirus. Various releases in the past year saw heavy changes to Brute Ratel’s core to adapt the several detection techniques brought in by EDRs such as threat intelligence based detections via ETW, hooking of library loads, hooking syscalls via <a href="https://learn.microsoft.com/en-us/windows/win32/etw/syscallenter">Syscall Enter event tracing</a>, detections to sleep masking and more. To perform these types of research, we need access to various EDRs and understand how they detect the payloads. We were able to achieve evasion by reversing some EDRs provided to us by our customers, or by purchasing some EDRs ourselves. Apart from just building Brute Ratel, we also have to continously monitor for malicious usage of the product, continous changes to the encryption methods for licensing to make it hard for anyone to crack the product licensing, and also the <a href="https://www.qt.io/pricing">licensing on the QT-GUI part</a> all of which come at a cost. Keeping all of this in mind, the price of Brute Ratel is now increased from 2500 US$ to <strong>3000 US$</strong> for all new purchases starting from February 1st 2023 and all renewals starting next year will also cost the same. However, for our existing valuable customers, the renewal costs would stay the same i.e. 2250 US$ for this year. For customers who have already subscribed to a multi-year license, they will be unaffected till their next renewal. Make note that the pricing is inclusive of all taxes and is still lower than it’s competitors, while still providing more features, instant Discord/Google Meet support and reliability.</p> <h3 id="pe-reflection-not-rdll">PE Reflection (not RDLL)</h3> <p>The first major feature update is reflection of unmanaged executables. Badger can run any unmanaged executables compiled in Clang or Mingw within its own memory. This helps to avoid process creation events or creating new process as a whole. This feature of running executables instead of reflective DLLs in-memory was first introduced in release <a href="/release/2022/07/20/Release-Stoffels-Escape/">v1.1</a> and before that in a blog that I posted quite a while back <a href="/research/feature-update/2021/01/30/OBJEXEC">Executing Shellcode from Object Files/PE</a>. However, implementing this feature without making changes to msvcrt.dll’s function was a bit of a challenge. Some executables call ExitProcess when they return, and the entrypoint of executables are usually mainCRTStartup from msvcrt.dll instead of ‘int main()’ or ‘void main()’. This meant that the badger should be capable of handling the Exits and it should not exit itself when the in-memory executable process returns. One way to pass commandline argument to the in-memory process’s mainCRTStartup was patching a few functions from msvcrt.dll, but that boat is long passed for Brute Ratel as we don’t like patching things in memory in order to avoid getting caught. All of this is now possible with badger with the introduction of the <code class="language-plaintext highlighter-rouge">memexec</code> command.</p> <p>The <code class="language-plaintext highlighter-rouge">memexec</code> command can run any console executable in memory and return the output of the executable using a custom in-proc-console-reader. Below are the screenshots of running mimikatz and a few other sysinternals tool:</p> <p>Executing mimikatz.exe coffee command in memory:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_coffee.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_coffee.png" /> </a> </div> </div> <p>Executing handle64.exe executable from sysinternal toolkit to list open lsass handles:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_handles.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_handles.png" /> </a> </div> </div> <p>Executing accesschk64.exe executable from sysinternal toolkit to check object access:</p> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="/css/ratelwarroom.css" /> </head> <div class="center-row"> <div class="card"> <a href="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_accesschk.png"> <img class="card-img-top img-fluid" src="/images/post_img/2023-01-04-Release-Blitzkrieg/memexec_accesschk.png" /> </a> </div> </div> <p>These are just simple examples to run various executables in memory and brings us a full round circle with all types of reflection in Brute Ratel.</p> <h3 id="socks5-with-udp-support">Socks5 with UDP Support</h3> <p>The earlier Socks4 in Brute Ratel is now upgraded to provide full support for socks4a and socks5. An operator can now select whether to use Socks4a or Socks5 when starting the socks proxy server. There is also support for UDP, DNS resolution and Socks5 authentication. The socks technique in BRc4 has undergone several changes and is now a part of the core badger itself unlike earlier where the socks client in BRc4 made a seperate HTTP connection. This ofcourse removes the capability of a seperate socks profile which was present earlier, but at the same time provides more stealth as the new Socks implementation supports using socks while sleep masking is active.</p> <p>The below video summarizes all the features for this release.</p> <link rel="stylesheet" href="/css/myblogs.css" /> <iframe src="https://www.youtube.com/embed/mg4i-BYM1XA" title="YouTube video player" frameborder="1" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe> <p><br /></p> <h3 id="adhoc-updates">Adhoc Updates</h3> <p>There are several other backend updates which are provided to the badger and the user interface apart from the major features above:</p> <ul> <li>Added ‘note’ feature to Commander which can display a note against each badger in the badger’s tab</li> <li>Added ‘clear_q’ command to badger’s terminal to clear commands in queue</li> <li>Added heavy updates for EtwTI evasion</li> <li>Improved command-line parsing for sharpinline, coffexec, loadr and other arguments which execute C#/BOF or reflective DLLs</li> <li>Badger’s external IP in the Commander will now show CF-Connecting-IP or X-Forwarded-For in the Commander if added</li> <li>Added option in Commander to hide badger columns which are saved on the operator’s system</li> </ul> <p>Starting from this year, there will only be one major release per month, and the rest of the releases will be minor features updates or bug fixes/QOL.</p>Chetan Nayak[email protected]Brute Ratel v1.4 codename Blitzkrieg is now available for download. This release brings in a few new features, updates to EtwTI evasion techniques, and user experience (QOL) requested by the BRc4 community. A quick summary of the changes can be found in the release notes.