Colibri Loader makes use of common malware techniques but presents a new entry into the malware as a service market with some interesting functions.
It has been close to a year since my last blog post, many things have happened in my personal life since then and have kept me quite occupied. Due to this I have worked with the incredible Casperinous to produce this post. Casperinous researched the malware and passed a report to me that I then edited a uploaded to create this post!
Colibri Loader is a malware as a service that offers a residential loader along with a control panel written in PHP to give ease of use to the purchasers. The malware author claims to have written the malware in C/ASM and prices their creation at $150/week or $400/month. The malware offers the following functionality:
The malware was put up for sale on 27/08/2021. It has numerous positive reviews.
When beginning to analyze Colibri we see a lot of issues within the disassembly, such as unrecognized functions and invalid call opcodes. To fix the opcodes a potential solution may be to undefine then redefine but we can take this one step further and use the Create function option. This function in IDA can define the function and set the functions' stack and variables. Unfortunately Colibri is using in-proper opcodes (0xb8) which casues errors during function definition. Our solution to this is to NOP (null opcode) the in-proper opcodes and then use the create function tool within IDA.
This clears up the analysis significantly and reveals the entry point of the loader. The malware begins by loading DLLs and resolving functions. To load DLLs Colibri uses LoadLibraryW and makes use of hardcoded arrays which contain the name of the DLL. Once the exports of the chosen DLL have been located the malware will use a custom hashing algorithm to create a hash of the export name. (See figure 2)
Figure 2: Hashing function
Colibri's important strings are XOR encrypted within the binary and when retrieved to be used by Colibri will be decrypted. Sometimes the string is unencrypted and will be stored within the key part of the encrypted strings structure. If the string is unencrypted then Colibri will return the key instead of proceeding with the decryption process.
Before carrying out important functions Colibri makes sure that it hasn't been cracked by checking the hardcoded C2 with a hash. This is to make sure that someone hasn't changed the C2 in an attempt to reuse/repurpose the malware. If the check fails then the malware will exit.
Before continuing with the program flow, Colibri checks the language of the host system to determine whether they are within the CIS which it attempts to avoid. The malware acomplishes this by calling pGetUserDefaultLangID and then comparning the results to the following. If Colibri finds a match it will exit.
Language | Code |
Russian | 1049 |
Belarusian | 1059 |
Georgian | 1079 |
Kazakh | 1087 |
Tajik | 1064 |
Uzbek | 2115 |
Ukranian | 1058 |
Unknown | 106 |
After the language checks have been passed Colibri will attempt to reach the C2 and check that it is alive. Before reaching out to the C2, Colibri generates a unique identifier for the C2 that is calculated based on the serial number of the infected workstation. Once the UUID is generated the malware will send a request to the C2 gate with a "check" command, if the check fails and the C2 doesn't reply or does not reply correctly the malware will exit.
The general network communication of Colibri can be described as the following:
Colibri has 3 type of commands that are sent within its HTTP requests:
Command | Description | Response |
check | Checks the availability of the C2 server but also whether the workstation has been infected in the past. | The loader accepts the string “SUCCESS” as a valid response. |
update | Sends information about the infected system. | Colibri doesn't validate the response |
ping | Requests a task from the C2. | If there is a task within the C2 it will respond with it. |
When Colibri checks that the C2 is alive it will use the check command. Once the request is sent and a response is received the malware will decode the response using base64 and then use RC4 to decrypt the response. Once the response has been decrypted it will be compared to "SUCCESS". If the string and response do not match then the malware will exit.
Figure 3: Handling C2 response
To maintain a presence on the infected system Colibri will move itself to a different filepath depending on the Windows version. Colibri checks if it is already in the destination and if not it will move to the following paths depending on the Windows version.
Figure 5: Determine persistence path
Depending on the Windows version Colibri will use the following paths:
Once moved Colibri will schedule a task with the following command and then exit.
Figure 6: Getting system information
Colibri encrypts the data with RC4 and then base64 encodes it. Then the encrypted information is POSTed to the C2.
Figure 7: Encryption of system information
Now that the infected system is registered to the C2 Colibri will send "ping" commands to the C2 to check for new commands and tell the C2 that the infected system is online. When a "ping" command is sent the C2 can return the response of "NUPD" which stands for NEED UPDATE, the C2 will respond this when it needs the malware to re-register the infected system. If the malware receives this response it will re-send the check in information to the C2.
If the malware does not get a response of "NUPD" then it will proceed to parse the response and determine what command it has received. The command is made up of four arguments that are separated by the '|' character. The command has the following structure.
ID | Name | Description |
1 | Command ID | The ID determines how the command is handled and what data to use. |
2 | Payload URL | The URL of the fie that Colibri will attempt to download and execute. |
3 | Payload Arguments | The arguments that will accompany the payload. Usually this is used when the payload is a DLL and Colibri needs to know what export to use. |
4 | Use admin privileges | Determines if the payload is to be ran with elevated privileges |
Examples of commands found from public sandboxes:
Colibri determines what function to call based on the first argument and will dispatch what command to use depending on what number it is.
Command ID | Description | Parameters |
1 | Download the payload and delete the file zone identifier. Then execute the payload with rundll32. | Payload URL + Args |
2 | Download the payload and delete the file zone identifier. Then execute the payload with regsrv32. | Payload URL + Args |
3 | Download the payload and delete the file zone identifier. Then load the payload with LoadLibraryW | Payload URL |
4 | Creates a thread the injects the payload into it | Payload URL |
5 | Executes a command with cmd open | Args + Command |
6 | Cleanup infection by deleting persistence and removing itself. Also executes command. | File Path |
7 | Same as 6th but doesn't execute command | None |
0 | Download the payload and delete the file zone identifier. Then execute the payload. | Payload URL + Args + Admin Rights Flag |
Commands 0 to 3 are all related to downloading and executing a payload. The malware retrieves the payload with the User-Agent "GoogleBot". After downloading the payload, Colibri deletes its file zone identifier and then based on the id, the payload is executed.
Figure 8: Call command depending on ID
The command id 7 is forcing the loader to delete its persistence mechanism, the scheduled task but also remove itself from the system. The removal is being achieved by using ShellExecuteW to execute the following command:
Command id 5 executes the third element in of the ping response arguments by using the ShellExecuteW API and calls "cmd open".
Figure 9: Commands 7 & 5
The command id 6 borrows elements from the command id 7, but before deleting itself, Colibri executes a file with “CreateProcessW” API.
Figure 10: Delete itself
Lastly the command id 4 is responsible to download the payload and inject it to the current memory space. The injection is simple; The malware allocates space, then copies the executable and is setting the correct memory permissions on each section, rebuilds the import directory, rebase the code based on the new image base and then transfers the execution to its OEP.
The malware does not demonstrate innovation but certainly shows that sticking to the basics will create an effective piece of malware. Colibri is not a common malware seen in the wild and does not seem to be holding up its competition with the likes of Smoke Loader and Amadey. The malware is not without its flaws but the developer also indicates that they are willing to continually update their creation. I'd like to extend another thank you to the amazing Casperinous without him this blog post could not have been made, please check him out. Thank you for reading and see you in the next blog post!
Tools used to analyze Colibri: https://github.com/Casperinous/colibri_loader