In a previous article, we introduced a piece of malware that ThreatSpike detected in December 2020, moving laterally between hosts. The attack consisted of two components: A text editor repurposed as a launcher for the actual payload, identified as Cobalt Strike’s Beacon. A compromised domain controller attempted to plant functrl64.exe, a maliciously modified version of the popular Notepad++ editor, followed by functrl32.dat, at first an unidentified data file, in the system directory of a victim machine.
During our analysis, we identified functrl32.dat to be a Dynamic-Link Library (DLL), hidden behind two layers of encryption. We removed each layer and revealed the payload that the malware attempted to install and execute on the machine:
Figure 1: Export table of functrl32.dat with the original filename and the only entry in the export list (Ghidra).After deployment on a victim machine, the DLL connects to a C2 server and begins polling for further instructions. The way this malware seeks to avoid detection is by loading it from inside process memory without the help of the Windows image loader that would normally be required to load executable modules. Instead, the DLL uses a technique called Reflective Loading that we described in depth in another article. The article left off where the DLL has been covertly loaded into functrl64.exe’s process and where we got to execute the payload.
Following the Path of Execution
functrl32.dat, or beacon.dll - as we are going to call it from here onwards - exports only one function, which has precisely one task: Deliver the payload. But if it is not exporting any other functions, then where is the payload to be found? A DLL, like any other Portable-Executable (PE) file, has an entry point - a function called by the operating system, after the PE file has been successfully loaded. In the case of DLLs, this function is aptly called DllMain. It serves to notify the library that it is attached to a process, and to give it an opportunity to initialize any resources that it requires.
A malware author could choose not to cooperate by not yielding control back to the owning main executable. Although, DllMain was not intended to be used like this, it allows a DLL to implement a program in the same way that a main function does for executables. This is precisely what beacon.dll does. The reflective loader calls DllMain twice: Once, to notify the DLL that the library has successfully loaded and that it can now perform its own internal initializations. The second time it is called with a notification that is not part of the ordinary Windows API.
Figure 2: DllMain function of beacon.dll (Ghidra).From [1] in Figure 2, a notification code of 4 is shown, which is not recognized by Windows. This is similar to the behaviour of another well-known penetration testing tool:
Figure 3: Metasploit example of customly defined notification codes used in DLL injection (Github).Metasploit defines custom notifications to execute payloads delivered via injected DLLs. This gives an initial hint as to what kind of tool the attackers are using. The payload itself is finally called at [2] in Figure 2.
Calling Home
We are looking for the beacon inside beacon.dll and with it any information about the origin of the attacks. Specifically, we are looking for network signatures and other potentially damaging functionalities that may not have been associated with the attack.
Initially, for a significant number of instructions the beacon does not appear to interact with the host machine. Instead, it parses additional configuration settings that had been hidden in the DLL. Without further context, at this point the meaning of these settings is difficult to extrapolate. However, as the parser is moving to the data sections of the DLL and beyond, strings and clear text begin to appear that were unreadable before.
Figure 4: IP address and query path revealedFigure 4 shows that the beacon reveals fragments of HTTP request information. More importantly, an IP address appears: 88.218.92.19. Next, beacon.dll enters a polling loop, where it attempts to connect to this IP. It does this using a HTTP GET request, which is entirely constructed from information statically stored inside the DLL. Before we focus on these HTTP requests, some passive enumeration on the IP is performed using shodan.io, shown in Figure 5.
Figure 5: Shodan profile of the discovered IP addressShodan indicates that TCP ports 22, 80, 443 and 50050 are open for this IP address. The first three of these ports are well known for SSH, HTTP and HTTPS services respectively, however the last port is uncommon. As mentioned here, port 50050 is the default controller port for the Cobalt Strike Team Server. This evidence suggests that the discovered IP is the C2 server used by the attackers. Cobalt Strike is a form of all-in-one threat emulation software, offering reconnaissance tools, attack packages and post exploitation capabilities for a hefty price tag. It has a dedicated payload called Beacon, similar to how Metasploit has its Meterpreter payload. Further evidence to suggest the involvement of Cobalt Strike comes from the query path, shown after the IP in Figure 4 (/s/ref=nb_sb_noss_1/167-3294888-0262949/field-keywords=books). This string is part of a Malleable C2 profile mimicing Amazon web traffic, shown here. Malleable C2 profiles are used for covert communication by specifying how network traffic between Cobalt Strike’s Beacon and the C2 server should appear. This allows Beacon to blend in with legitimate looking traffic, as demonstrated in Figure 6, which shows the HTTP requests that were sent to the C2 server. Although the Host header suggests the request is being sent to www.amazon.com, the actual traffic is sent to the C2 server at 88.218.92.19, determined by the destination address in the headers of the underlying IP packets. From Figure 7, we learn that metadata about the communication between Beacon and the C2 server is obfuscated in the Cookie header. Specifically, the following steps take place:
- The metadata is Base64 encoded
- The string “session-token=” is prepended
- The string “skin=noskin;” is prepended
- The string “csm-hit=s-24KU11BB82RZSYGJ3BDK|1419899012996” is appended
- The entire result is placed in the Cookie header
Upon receiving the HTTP request, the C2 server would obtain the original metadata by extracting the Cookie header, removing the prepended and appended strings, then finally Base64 decoding what remains. The server then uses this metadata to determine which beacon has reached out to it and sends a response back with the corresponding commands to be run on the victim’s machine. Figure 7 shows that the C2 server also includes certain headers in its response to blend in with Amazon traffic. The output field simply contains the Malleable C2 print statement, indicating that the C2 commands are to be sent as-is in the response body, without any obfuscation. It is through this mechanism by which the malware under analysis would have covertly communicated with the C2 server.
Figure 7: The http-get section of the Malleable C2 Amazon profileAt the time of writing, it is noted that the domains asvxz-100.shop and listbookss.buzz resolve to the IP address of the C2 server. The sites at these domains seem malicious at first glance as shown in Figure 8. They advertise money-making schemes via WhatsApp and are likely used for phishing. The source code of the HTML files contains Chinese characters which could hint towards the origin of the attackers.
Exploring Cobalt Strike’s Beacon instructions
All the evidence suggests that beacon.dll is the Cobalt Strike Beacon malware. To understand what this malware is capable of; we analysed the DLL further. At the core of beacon.dll is a large switch statement containing cases to handle the client-side execution of C2 commands, shown in Figure 9. Each integer represents an instruction’s opcode, hence branches in the switch statement would be processed depending on the instructions sent by the C2 server. Many of the opcode cases were analysed to determine the client-side functionality of the Beacon.
Starting with the basics, the function for opcode 0x5 makes a system call to SetCurrentDirectoryA, shown in Figure 10, suggesting it is the most likely candidate for Beacon’s cd command. Another simple instruction was opcode 0xa, which would write to a file by first declaring a variable to have the string value ‘wb’ (write binary), and then jump elsewhere to call fopen, fwrite and fclose.
Figure 10: The client side function which executes Beacon’s cd commandFigure 11 shows a function which gets called for opcode 0x31. This is responsible for attempting to log a user onto a machine, and impersonating it’s security context to then lookup the account name of the associated SID and send it back to the C2 server. This might seem redundant at first, but this type of functionality could be used to easily test login credentials for a given user on the victim’s machine. The account name result is eventually sent back to the C2 server via send_http_requests_with_impersonation, which makes system calls to HttpOpenRequestA and HttpSendRequestA.
Figure 11: The try_log_on_user function is called for opcode 0x31Beacon’s shinject command injects code into a remote process and is likely linked to the instruction identified by opcode 0x63. The code for this instruction first creates a process using the CreateProcess or CreateProcessAsUser system calls, and then injects shellcode into it in one of two ways. Process injection generally consists of three stages: allocating memory, writing to that memory and then finally executing it. The two functions in Figure 12 only refer to the first two stages of process injection: allocating and writing memory.
Figure 12: Two methods are found to handle the allocation and writing of memory for remote process injectionInitially, we will focus on line 16 in Figure 12 because it follows a classic approach. As shown in Figure 13, this function uses the following system call pattern:
VirtualAllocEx -> WriteProcessMemory
The call to VirtualAllocEx returns the base address of the allocated region of pages in the target process. This base address is then used in WriteProcessMemory to specify the location in the remote process to which the shellcode (src_buffer) should be written. The next step would be the execution of the written memory, hence to make sure this is possible the pages must have the relevant memory protections specified. A call can be seen to VirtualProtectEx which sets new protections to the memory that was just written to, likely making the region executable. The system call pattern used here is the bread and butter of process injection used in red teaming. Cobalt Strike’s Beacon malware uses this as its default setting.
Figure 13: The inject_to_remote_process_using_VirtualAllocEx functionThe other way in which Beacon handles the allocation and writing of memory for process injection is shown in Figure 14. This method uses a different system call pattern:
CreateFileMapping -> MapViewOfFile -> NtMapViewOfSection
To understand this system call pattern, it is important to be familiar with file mappings. Figure 15a shows a file mapping from a file on disk to a file mapping object (or section object) in physical memory. A process can then map part of this section object into its own address space, referred to as a view, and access the file contents.
Figure 14: The inject_to_remote_process_using_NtMapViewOfSection functionWhen beacon.dll performs process injection via this system call pattern, the following takes place:
- It uses CreateFileMapping to create a file mapping that is backed by the Windows system paging file (C:\pagefile.sys) instead of a file in the file system, see Figure 15a.
- It then calls MapViewOfFile to map a view of the section object into the current process, see Figure 15b.
- It copies the shellcode from a local buffer into the view using memcpy. Due to the mapping, the shellcode now also exists in the section object, as highlighted in Figure 15c.
- Calling NtMapViewOfSection would cause the section object’s memory to be mapped to the remote target process, see Figure 15d.
The remote process therefore also ends up containing the shellcode in its address space. This system call pattern is not a new technique, but since the NtMapViewOfSection system call is no longer officially documented by Microsoft, it is important to remember its existence.
Summary
In this article, we discovered that beacon.dll is actually the Cobalt Strike Beacon payload, and that once a process loads and passes execution onto this DLL, it would attempt to reach out to a C2 server at 88.218.92.19. Enumeration on this IP suggested it was still being used by the attackers for malicious activity, as two suspicious domains were found to be registered to it. We also looked at Malleable C2 profiles and how this instance of the Beacon payload blends in with Amazon related HTTP traffic when it reaches out to the C2 server. Finally, we examined Beacon’s capabilities to understand what commands could be run by the C2 server if a connection was to take place.
Although Cobalt Strike is a tool meant for red team engagements, it is often seen being used in the wild by attackers. Traces of its Beacon malware have been identified in several high-profile attacks, one of which being the recent SolarWinds Orion Supply Chain attack where it was discovered that a custom Beacon would be deployed by the Teardrop memory dropper on a victim’s machine. It has become increasingly valuable for blue teams to detect Cobalt Strike activity, however since the attacker can profile the C2 traffic as they wish, this is no easy task. As a minimum precaution, network traffic can be monitored for the signatures of the most used profiles. To conclude this article, the signatures of the Malleable C2 Amazon profile are shown in Figure 16, alongside the other IOCs of this attack. Altogether, the IOCs consist of the C2 server’s IP address and registered domains, the remnants of the Malleable C2 profile used, and finally the hashes of the malware files.
(2) listbookss.buzz
(2) csm-hit=s-24KU11BB82RZSYGJ3BDK|1419899012996