Krakz
Malware hunting & Reverse engineering notes

BumbleBee notes 🐝

pbo

BumbleBee is categorized as a Loader, the malware is used by Initial Access Brokers to gain access in targeted companies. This article aims to summarizing the different TTPs observed in campaigns distributing BumbleBee and provides a script to extract its configuration.

TL;DR BumbleBee #

The loader delivers diverse payloads (e.g: Cobalt Strike, ransomware, etc), the operators of BumbleBee have been named EXOTIC LILY by the TAG in a report published in March 2022.  Google TAG article mentionned BumbleBee Loader (e.g: The user-agent set to bumblebee, hence dubbed BUMBLEBEE. https://blog.google/threat-analysis-group/exposing-initial-access-broker-ties-conti/ Moreover, similarities with other loaders in terms of operation have been noticed notably with IcedID and Emotet. Code similarty (hook installation) with Trickbot have been observed and explained in the post The chronicles of Bumblebee: The Hook, the Bee, and the Trickbot connection. The malware is well documented by now (March 2023) as evidenced by the number of reports on malpedia.

BumbleBee capabilities #

The malware has a custom unpacking mechanism, it manipulates hooks to setup its execution chain, the loader uses multiple environment detection techniques because of the complete integration of the project al-khaser  al-khaser is a PoC “malware” application with good intentions that aims to stress your anti-malware system. It performs a bunch of common malware tricks with the goal of seeing if you stay under the radar. . It communicates with its command and control over HTTP. Since August 2022 the malware embeds a list of IP addresses in its configuration, some of them are legitimate IP addresses, this technique is also used by other malware such as Emotet and Trickbot.

BumbleBee command and control IP addresses, port and the bot (or botnet) identifier are stored in the .data section, obfuscated with the RC4 encryption algorithm. A script to extract and deobfuscate them is provided at the end of this post.

Campaigns file format #

First malspam campaign which delivered BumbleBee contains a web link to a protected ZIP archive.

  1. The archive contains an ISO file;
  2. The ISO contains a LNK file and a DLL file;
  3. The LNK executes rundll32.exe to invoke the embedded DLL;

Figure 1: BumbleBee infection chain with ISO file

Figure 1: BumbleBee infection chain with ISO file

This model of campaign was used for months. During the summer of 2022, actors updated the disk image format from ISO to VHD. Content of disk image (VHD) changed too, the DLL is no more stored as a file, but it is embed obfuscated in a PowerShell script. The script is executed by the LNK with the execution policy set to bypass. The BumbleBee’s DLL is stored in the PowerShell script in obfuscated strings (e.g: $elem30=$elem30.$casda.Invoke(0,"H")). After strings replacement, the base64 encoded variable is decoded, decrompressed (ungzip) and invoked (e.g: scriptPath | iex).

Figure 2: BumbleBee infection chain with VHD file

Figure 2: BumbleBee infection chain with VHD file

NB: File sharing service used to deliver BumbleBee change regulary e.g.: WeTransfer, Onedrive, Smash, etc. Details of a campaign using onedrive file sharing website are written in the article: Bumblebee DocuSign Campaign.

Examples IOCs:

Configuration extractor #

As introduced above, the configuration is stored encrypted with the RC4 algorithm.  RC4: Rivest Cipher 4, also known as ARC4: https://en.wikipedia.org/wiki/RC4 The key is in cleartext in the binary and its length is repeatedly (for BumbleBee case) fixed to 10 characters.

Here is the two functions that implement RC4 algorithm in BumbleBee:

Figure 3: BumbleBee implemenation of PRGA of RC4 algorithm

Figure 3: BumbleBee implemenation of PRGA of RC4 algorithm

Figure 4: BumbleBee implementation of KSA of RC4 algorithm

Figure 4: BumbleBee implementation of KSA of RC4 algorithm

The key is stored at the end of the blob of data containing the encrypted list of IP addresses. After analysing few samples of BumbleBee, it appears that the blob of data containing the IP addresses is always 4105 bytes long (plus one null byte) which is a pattern to look for in the DLL for a C2 extractor.

Figure 5: Location of the blob and the RC4 key

Figure 5: Location of the blob and the RC4 key

The script below attempts to loop over data until a blob matches the blob size, then it extracts the RC4 key (the last 10 bytes of the blob) to finally decrypt the data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import ARC4


def decrypt_rc4(key: bytes, ciphertext: bytes) -> bytes:
    """Decrypt RC4 encrypt data, `pip install cryptography`"""

    algorithm = ARC4(key)
    cipher = Cipher(algorithm, mode=None)
    decryptor = cipher.decryptor()
    cleartext = decryptor.update(ciphertext)

    return cleartext


def get_bumblebee_c2(data: bytes) -> bytes:
    """
    Command and Control are stored at the end of the .data section,
    the configuration of the obfuscated C2 and its associated RC4
    are stored in the same blob with a fixed lenght of
    4105 plus one null byte (4106).
    !\xac\xd2\xfe=;\x87\x94\xebP\x8e@\x08}\x00/^I\xd4\x86\xaf\xd2\x14-
    \x16\x89A\xa9uT\x00\xbduC\xb7\x9e~\x19\xac\x9f\xb4\x0f\xae>\xcc
    \x96S]\xb56\x93C\x9d*p\xed\xc9\x04:Oew\xc3*X`:a\xe0T\x8e\x93>\xf9
    \xf8\xe2\x17Q\x15b,8\xa8[\xf5N\x93\xffMM]\x8d\xec\xde\x13\x95z\xc3
    ...
    ...
    ... <redatacted> ...
    \xd4\x00\xa1xZ:\x1e\x90\x00X\xea\xca\x0c\'\xee\xffOR5tw\xc0I\x86R"!
    \xf8\xa3\x87\xc8\x16Mo_5\x82_\x81\x9f<RC4 key composed by 10 bytes>
    """

    c2 = b""

    for blob in map(lambda x: x.strip(b"\x00"), data.split(b"\x00" * 4)):
      if len(blob) == 4106:
	  key = blob[-10:]
	  ciphertext = blob[:-10]
	  c2 = decrypt_rc4(key, ciphertext)
	  c2 = c2.replace(b"\x00", b"")
	  print(f"BumbleBee Command and Control IoCs: {c2}")

    return c2


if __name__ == "__main__":
    import sys

    with open(sys.argv[1], "rb") as f:
      get_bumblebee_c2(f.read())
Code Snippet 1: BumbleBee C2 extractor

PS: Tested with the package cryptography with the version: 3.4.8.

Go head and re-use, adapt the script for your needs!

Resources #