# The chronicles of Bumblebee: The Hook, the Bee, and the Trickbot connection **[elis531989.medium.com/the-chronicles-of-bumblebee-the-hook-the-bee-and-the-trickbot-connection-686379311056](https://elis531989.medium.com/the-chronicles-of-bumblebee-the-hook-the-bee-and-the-trickbot-connection-686379311056)** Eli Salem April 27, 2022 [Eli Salem](https://elis531989.medium.com/?source=post_page-----686379311056--------------------------------) Apr 27 17 min read In late March 2022, a new malware dubbed “Bumblebee” was discovered, and reported to be distributed in phishing campaigns containing ISO files which eventually drop DLL files that contained the Bumblebee malware itself.[1][3]. ----- This malware deployment technique is not new, and several other malware has already been observed using it, most notably: BazarLoader, and IcedID[3]. Also, similar to the aforementioned malware, Bumblebee too was observed delivering the Cobalt-Strike framework. From a threat research perspective, what makes this malware interesting is the fact that it was associated with the Conti ransomware group as one of the group's threat loaders[1]. In the past, the traditional loaders of Conti were Trickbot, Bazarloader, and Emotet, so it was quite intriguing to inspect this malware closely. In this article, I will present a code analysis of the Bumblebee malware, obviously, due to the malware's large size I will not cover everything, and will focus on the parts that I think are the most interesting in terms of capabilities. Also, one of my favorite topics in malware research is the ways of malware to avoid detection, so I will put more emphasis on this subject as well. Lastly, I divided the entire article into three parts, the table of contents is the following: 1. 2. 3. ## PART 1 The Hook: Unpacking the bumblebee’s crypter Hash: a9c8b7c411571700e6ea03e4e48ddb896a33e53e ----- Bumblebee dropper as seen in PEstudio The initial dropper of Bumblebee is a 64bit file, with relatively high entropy which indicates a possibly obfuscated \ encrypted content that will be decrypted in runtime. The DLL itself contains two export functions: InternalJob and SetPath. Also, the file’s internal name appears to be “lodqcbw041xd9.dll”. Bumblebee dropper exports and internal name in PE-Bear ## Unpacking mechanism ----- Once we enter the loader s main function, we see that it is unique, and does not look like any common crypters that can be found in Conti’s loaders (such as Emotet or Bazarloader). Bumblebee loader\crypter main As we open the loader in IDA, we see that the majority of the PE in the IDA navigator has the olive color which means unexplored bytes. This is common when there is some content in the PE that needs to be decrypted during runtime. Bumblebee loader unexplored bytes **Tip: During my analysis, I disabled the file’s ASLR to match the addresses in IDA and Xdbg,** this is super helpful and saves a lot of time. To do so, open the file in CFF explorer, and then: 1. Click Optional Header 2. Go to DllCharacteristics 3. Remove the V from “DLL can move” ----- Disabling ASLR Next, we can see that the DllEntryPoint is an empty export function, so we will want to redirect our execution flow to one of the working export functions, for this case, we will choose “SetPath”. To redirect the flow, do the following: 1. In IDA \ PE-Bear, copy the address of the required export function 2. In Xdbg, right click on RIP 3. Click on “Modify Value” 4. Paste the address of the export function ----- Changing the address After clicking OK we will find ourselves at the beginning of the export function. This trick can be used in any other malware the executed via designated export function Bumblebee SetPath From a reverse engineer perspective, the crypter is an inconvenient binary to inspect, and there are not many “quick wins” we can gather just by looking at it, however, this crypter is unique in today's landscape so I will focus on the areas I found are the most interesting. First, the crypter will start with a traditional unpacking activity, in the function sub_180003490 there are two other functions: 1. - which will allocate virtual memory using (this function will happen multiple times during the crypter unpacking) 2. - Which gets an embedded content and writes it into the newly allocated memory ----- Bumblebee loader\crypter main Then, the function sub_180002FF4 will be executed to do the following: 1. Allocate new virtual memory using the same function. 2. Manipulate the content from the first allocated buffer and write the output into the newly allocated memory Bumblebee loader\crypter main The next step will be the function sub_180004180, this function will do the following: 1. It executes a function named that will allocate multiple virtual memories using the already mentioned . 2. Call the function named that will use the virtual memory that was allocated in, do additional manipulations, and eventually writes an unpacked MZ into the last allocated buffer from the function . ----- Bumblebee loader\crypter main When looking statically in the function sub_180003CE, the loop that will write the unpacked file will be the following: Bumblebee loader payload decryption And when observing dynamically, it will look like the following: Bumblebee loader payload decryption In the end, we get an allocated memory with Read-Write permissions with an unpacked payload inside. ----- Bumblebee loader payload decrypted in process hacker Until now, everything that is observed are things that are pretty much common in other loaders \ crypters, however, we still have two unsolved questions: 1. The code section of the payload does not have Execute permission, so it cant run. 2. What makes this loader special? ## Enters the hook The loader will enter a function called sub_180001000, this function will create inline hooks[5] that will ignite the chain of events that will lead to the code execution. ----- Bumblebee loader payload decryption As we enter, we notice something interesting, the loader assign functions to a memory address, then it will call another function named sub_100025EC. Assign functions to addresses This function will do the following: 1. Get Ntdll handle with 2. Get the address of 3. Get the address of 4. Get the address of 5. Return the data ----- Getting NT functions To observe it dynamically, we can just go to the debugger and step over the functions themselves. Getting NT functions After exiting sub_100025EC, our attention will go to a function named sub_1800037C4. This function will be responsible to install a hook in the aforementioned NT functions. It will do it in the following way: 1. Call to change the protection of the area it wants to write into to be writeable 2. Call that will take as arguments:1. The function to write into 2. The content it wants to write3. The size 3. Call to change the protection again to not be writeable ----- Setting hook Eventually, this activity will occur inside a loop to install the hooks in each of the NT functions. The hook that will be installed will be the functions that have been assigned to memory addresses at the beginning of the larger function. 1. for NtMapViewOfSection 2. for NtOpenFile 3. for NtCreateSection If we wanted to observe the changes dynamically we have two options, the first one is to just observe it in the debugger by step over sub_180002978 Hooked NT functions Another option is to use the took Hollow hunter[6] with the “/hooks” as an argument. Then, we will have a .tag file from the hooked DLL (if found of course) ----- View hooks using hollow hunter And when we open this file with a text editor we could see the indication of who are the hooked function, and where the hook itself lies. View hooks using hollow hunter To summarize the hooking procedure, it will look like this: ----- Bumblebee loader install hook mechanism ## Executing the code After we finish setting the hooks, we will head to the function sub_1800013A0 Bumblebee loader\crypter main This function will attempt to execute the DLL “GdiPlus.dll” using the API call LoadLibrary, with SetPath as an export function. LoadLibrary loading GdiPlus.dll LoadLibrary loading GdiPlus.dll **Q: Why does the malware even want to use GdiPlus.dll?** **A: It doesn't.** **Q: So why the need to load it?** **A: Because it is not loaded (wait what?!)** The malware will attempt to use some (and unique) custom unpacking: 1. When loads a DLL file, it uses internally the hooked NT function as part of its internal activity. 2. The malware chooses a DLL that is not loaded yet. ----- 3. will get a file handle of 4. will create a section for the file handle of However, here is when things become tricky, when the LoadLibrary will try to use _MapViewOfSection to map the GdiPlus.dll section, the hook function of MapViewOfSection_ (sub_180001D4C) will do the following: 1. It will use to create a new section with READ-WRITE-EXECUTE permissions, without any file handle to associate it with. 2. It will write the unpacked malicious content into this section 3. It returns NTSTATUS_SUCCESS to the so it will seem to it as if was mapped successfully. Hooked NtMapViewOfSection mechanism The result will be an unpacked bumblebee malware that resides in the RWX section and is associated with GdiPlus.dll. Interestingly, the GdiPlus.dll is considered a relocated DLL in Process hacker. ----- Relocated module point to RWX section ## Bumblebee dropper high lever summary If we want to look at all the dropper unpacking mechanism steps in a high-level overview and summarize them into three steps, it will look like this: ----- Bumblebee dropper overview ## PART 2 The bee: Investigating the bumblebee’s payload ----- Unpacked Bumblebee payload The unpacked malware is a large 64-bit file with quite high entropy. This file appears to be the core component of the Bumblebee malware. It features many traditional capabilities we would expect from malware, such as internet communication, file manipulation, collecting user information, cryptography libraries, etc. In my article I will not cover this file as much because of scoping decisions, however, some interesting code parts to mention are: ## Stolen anti-analysis code As with many malware, Bumblebee also has anti-analysis tricks, however, the majority of them are grouped in one large function. Also, During my observation, I notice that additional anti-analysis checks have been added as time goes by, which indicates a quick evolving malware or that the authors are still in the “testing the waters” phase. In addition, this entire anti-analysis function code is taken from the GitHub page of the “alkhaser project”[7]. For good measure, I will show some examples. **Searching for processes** The malware will search for multiple tools that are being used for dynamic and static malware analysis tools. The malware will iterate through the processes using _CreateToolHelp32Snapshot._ ----- Searching for processes in Bumblebee As said, this code is the exact code found in the al-khaser project. ----- al-khaser source code The malware also attempts to detect any kind of virtualization environment with the detection of their processes, it varies from Vmware to Vbox processes. Searching for Vmware processes in Bumblebee **Searching registry keys** ----- The malware will attempt to search for designated registry keys that indicate any kind of virtual environment from multiple products. Searching for Vmware registry key in Bumblebee **Searching file paths** The malware will search for file paths that can indicate any kind of virtual environment. Searching for VBOX files in Bumblebee At this point, it will be useless to continue writing the anti-analysis capabilities, so for those who want to see all, please visit the al-khaser project GitHub page. ## Executing processes Among the malware, capabilities are to execute Rundll.exe to run the DLL with the InternalJob as an export function using Wscript. ----- Executing Wscript Also, the malware can use PowerShell to perform further activities Executing PowerShell ## The little ones inside the flask One of the most interesting things about the Bumblebee core component is the fact that it contains two DLL files inside of him. ----- Two hidden DLL files inside the unpacked Bumblebee Both of these files have the same internal name RapportGP.dll (which is also used by the security company Trusteer) Bumblebee hooking DLL aka RapportGP.dll The two DLL files are completely identical except for the fact that one of them is 32-bit and the other is 64-bit. ## PART 3: The shadow of Trickbot- Investigating the hooking DLL In the last part, I will investigate the RapportGP.dll, as said, there are two versions: 32\64 bit, and for my analysis, I will focus only on the 32 bit. The main concept behind RapportGP.dll is hooking, and the entire module’s mechanism is supporting this activity. ## Check for existing hooks ----- One of the first activities of the module occurs in a function named _sub_100060C0, in_ general, this function will be responsible to check if there is any hooked function from a list of pre-determined functions. Inside sub_100060C0, the chain of events that leads to this is the following: 1. A handle to,,, obtained 2. The requested DLL’s path obtained 3. A call to the function made to get a copy of that stored in the allocated memory 4. The arguments are sent to another function named 1. RapportGP.dll checking and disabling existing hooks The functions it wants to check are: In Ntdll.dll ----- RapportGP.dll list of Ntdll functions to check In Kernel32.dll ----- RapportGP.dll list of Kernel32 functions to check In Kernelbase.dll RapportGP.dll list of Kernelbase functions to check In Advapi32.dll ----- RapportGP.dll list of Advapi32 functions to check In sub_10005B90, the module path of the requested DLL file will be mapped to memory and will be sent to an additional function named “sub_10005D40” that will deal with the actual checking. 2. RapportGP.dll checking and disabling existing hooks As for the checks themselves, it is quite simple: 1. The malware iterate through the export functions of the legitimate DLL file that was mapped to memory by the process when it loads. 2. The malware will check if the name is one of the function names it wants to check 3. Once found, the malware calls that checks for hooks evidence in the DLL that was mapped by the process loader 4. The malware will do the same for the DLL that was mapped by the malware itself (in ). 5. If no hooks are found, it will continue to iterate ----- 3. RapportGP.dll checking and disabling existing hooks And if there is an indication of hooks, the malware does the following 1. Get information about the original function 2. It will change the protection 3. Check if it's writable 4. Write the content of the mapped function to the original function. In this way, it restores it to the state it should be if there are no hooks. ----- 4. RapportGP.dll checking and disabling existing hooks If we wanted to observe this activity dynamically, all we need to do is to change the bytes from the beginning of one of the functions the malware wants to check. For example, let's take NtCreateFile. 1. Original function at 775222C0 2. The function that mapped by the malware at 02E022C0 5. RapportGP.dll checking and disabling existing hooks When looking in the dump, we can see that their code is exactly the same ----- 6. RapportGP.dll checking and disabling existing hooks Let's change the first byte of the original to have an E9 opcode (jump) 7. RapportGP.dll checking and disabling existing hooks Now, if we will try to debug dynamically, we will be able to get to the last part of the code. 8. RapportGP.dll checking and disabling existing hooks After stepping over memset, we can see that the E9 byte no longer exists and the original function returned to its normal state. 9. RapportGP.dll checking and disabling existing hooks At a very high level, the process eventually looks like this: ----- 10. RapportGP.dll checking and disabling existing hooks ## Setting the hooks After checking that there are no other hooks, the malware turns to set its own hooks. The malware will have two kinds of hooks for different purposes. ## First hooks: Disable Exceptions The malware will set a hook on the function RaiseFailFastException which is located in kernel32.dll and api-ms-win-core-errorhandling-l1–1–2.dll. The function that will be triggered will be empty, therefore no exception will be triggered. RapportGP.dll hooks to disable exceptions ## Second hooks: Further code execution ----- The malware will use the same technique the bumblebee loader did. It will first get the addresses of the function ZwMapViewOfSection, ZwOpenSection, ZwCreateSection, _ZwOpenFile, ZwClose, and LdrLoadDll._ RapportGP.dll second hooks And similar to the Bumblebee’s loader, it will first set the hook, and then will call LdrLoadDll which is the lower lever equivalent of LoadLibrary to load the module “wups.dll”, which will trigger the chain of events we already discussed in the Bumblebee loader part. RapportGP.dll second hooks ## The Trickbot hooking engine Although both hooks are doing completely different things, the hooks’ installation mechanism is the same. Interestingly, this mechanism is also the same as the web-inject module of Trickbot. ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module As with many ex-bankers that use hooking such as Panda, Trickbot, and Qbot, their hooking code is based on the Zeus leak, however, each of them has its flavor and changes and Trickbot is no different. In the Trickbot web-inject hooking mechanism, which has already been documented[8], when creating the inline hooking “trampoline” there is the following evasion technique: 1. Trickbot writes 35 bytes of NOPS (0x90) 2. Add the traditional function prologue 3. Write the jump to the targeted function at the end of the NOPS ----- Trickbot’s web-inject module evasion technique As we debug Bumblebee, we notice it uses the same unique evasion as well (adjusted for the API calls it wants to hook). So for example when hooking the ZwMapViewOfSection, which instantiates a Syscall, it will look like this. Bumblebee’s RapportGP.dll evasion technique And when targeting the user-mode functions RaiseFailFastException, it will look exactly like in Trickbot. ----- Bumblebee’s RapportGP.dll evasion technique ## Static differences and code evolution When inspecting the entire code flow graph of the hook installation function, we can see a striking similarity between Bumblebee’s RapportGP.dll and Trickbot’s web-inject module. ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module install hook functions Interestingly, although the actual functionality is the same, we might think that statically everything is the same, even the sub-functions inside the hooking installation function. Funny enough, this is not the case. As mentioned above, in the hooking installation function, one function is responsible for doing checks and return size (Please see the image above). 1. In Trickbot its 2. In Bumblebee its However, when inspecting their code and code flow statically, this is how they both look like ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module- same functionality, different flow Obviously, in Bumblebee, the authors have decided to use Control-flow-flattening[9] to obfuscate the entire flow of the function. For those of you who are not familiar with this obfuscation technique, I strongly recommend the following video[10]. In addition, inside each of these functions (sub_10001650 in Trickbot, sub_10002870 in Bumblebee) there are 3 functions (one of them is memset), and the Control-flow-flattening concept continues in Bumblebee inside them as well. For example, here are another two functions that act the same dynamically: ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module- same functionality, different flow When observing the two functions in Bindiff flow graphs, we could see some similarities. ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module-Bindiff ## Additional similarities In both modules, there are other functions that are not completely identical by code, however they serve the same functionality ----- **Example_1** Before entering the hooking functions, both Trickbot and Bumblebee attempt to use LoadLibrary and get the address of the function it wants to hook. The difference is that in Trickbot it explicitly writes “Kernel32.dll” and in Bumblebee it gets the DLL’s name from the caller function. Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module- same functionality, a different approach **Example_2** The call for the hooking activity looks very similar as well ----- Bumblebee’s RapportGP.dll vs Trickbot’s web-inject module **Example_3** Outside the hooking, the Bumblebee’s hooking module starts with getting the process handle and eventually duplicating a thread handle, whereas, the Trickbot’s module starts with getting the process handle and duplicating the token. Again, the same objective, in a different way. ## Customize flattened RC4 Another interesting activity lies inside the hooked ZwMapViewOfSection function. The hook appears to use a customize RC4 obfuscated with the Control-flow-flattening technique. ----- Custom RC4 with CFF obfuscation ## RapportGP.dll High-level summary When trying to summarize the entire file behavior, it eventually is the following: ----- RapportGP.dll overall activity ## Conclusion The bumblebee malware is a very interesting piece of code, and to perform their objectives, the authors show a high level of creativity and innovation. The interesting similarities between the Bumblebee hooking DLL and the Trickbot’s webinject DLL raise questions and speculations. On one hand, the similarities are not strong enough to deduce that the authors of Bumblebee and Trickbot are the same, on the other hand, it is not far-fetched to assume that the authors of Bumblebee have the source code of the Trickbot’s web-inject module. In any case, the authors took an already proven and working code and evolve it to be less detectable to AV products, and challenging to security researchers. ----- ## References [1] [https://blog.google/threat-analysis-group/exposing-initial-access-broker-ties-conti/](https://blog.google/threat-analysis-group/exposing-initial-access-broker-ties-conti/) [2] [https://twitter.com/Unit42_Intel/status/1512146449345171459](https://twitter.com/Unit42_Intel/status/1512146449345171459) [3] [https://www.cynet.com/orion-threat-alert-flight-of-the-bumblebee/](https://www.cynet.com/orion-threat-alert-flight-of-the-bumblebee/) [4] [https://github.com/hasherezade/pe-bear-releases](https://github.com/hasherezade/pe-bear-releases) [5] [https://youtu.be/9efJ8_ukxlY?t=2](https://youtu.be/9efJ8_ukxlY?t=2) [6] [https://github.com/hasherezade/hollows_hunter](https://github.com/hasherezade/hollows_hunter) [7] [https://github.com/LordNoteworthy/al-khaser/tree/master/al-khaser](https://github.com/LordNoteworthy/al-khaser/tree/master/al-khaser) [8] https://www.sentinelone.com/labs/how-trickbot-malware-hooking-engine-targets-windows10-browsers/ [9] [https://blog.jscrambler.com/jscrambler-101-control-flow-flattening](https://blog.jscrambler.com/jscrambler-101-control-flow-flattening) [10] [https://youtu.be/SulC2l1Dvbo](https://youtu.be/SulC2l1Dvbo) ## IOC bumblebee_dropper: 4a35fa2f0903f7ba73ac21564a5a0e2a25374e10 bumblebee_malware: 5dbb3bbc57653c348be7778628ed0ef11ffef35d bumblebee_rapportgp: 5c8f7465ba67138e58d3ca61e4346e31c2b799d8 Trickbot web-inject module: 0785D0C5600D9C096B75CC4465BE79D456F60594 -----