The PT Expert Security Center (PT ESC) has been monitoring the Cobalt group since 2016. Currently the group targets financial organizations around the world. Two years ago, for example, their attacks caused over $14 million in damage. Over the last four years, we have released several reports on attacks linked to the group.
Over the last year, the group has not only modified its flagship tools CobInt and COM-DLL-Dropper in conjunction with the more_eggs JavaScript backdoor, but also started using new methods to deliver malware and bypass security in the initial stages of the kill chain. As a group whose activities have long been of interest to security researchers all over the world, the attackers are highly motivated to stay one step ahead.
In 2019, the group conducted an average of three attacks per month. Although we do not know whether the attacks were successful, such frequency may indicate that the criminals possess substantial financial resources allowing them to maintain their infrastructure, update malware, and adopt new techniques.
The following histogram shows that in late 2019 the group started favoring CobInt over COM-DLL-Dropper.
The more_eggs JavaScript backdoor is detected by the ETPro ruleset, including in public sandboxes, whereas CobInt traffic does not trigger security mechanisms. In addition, CobInt downloads the main library from the command and control (C2) server directly to memory, while COM-DLL-Dropper saves to disk the obfuscated more_eggs, which is then executed in memory. Therefore, COM-DLL-Dropper leaves more artifacts on the infected machine.
1. European Central Bank phishing website
In late August 2019, we detected a CobInt attack that presumably targeted European financial institutions. We do not know whether the attack was successful. CobInt was dropped by a custom NSIS installer. We detected three versions of the dropper: for Chrome, Firefox, and Opera. Each dropper contained the same CobInt version and a browser-specific installer. Once launched, the dropper saved CobInt to the %TEMP% folder and then ran CobInt and the installer. Malware analysis proved that the droppers were distributed from the phishing website ecb-european[.]eu.
The site was a copy of the European Central Bank website, except for a pop-up window that asked visitors to update the browser.
Visitors who fell for the ruse downloaded the dropper to their computer. The page source code contained a link to the script that displayed the pop-up window.
The configuration strings in the script contain links for four droppers (we could not obtain the first one) and allow creating links for Safari, Edge, and Internet Explorer. The strings also show the window start time after loading the page, how many times the window will be shown to a user, type of device on which the window will be displayed, and which banner will be shown to the user. In addition, the script detects bots, crawlers, and spiders.
Here are alternative windows contained in the script:
We do not know how the user landed on this website. Most likely, the user would be a victim of a phishing attack like many of those performed by Cobalt.
The framework in question is not unique. We believe that Cobalt purchased it on a darkweb forum. In an article from November 2019, Zscaler described a similar scenario for spreading NetSupport RAT. The framework was placed on compromised sites, which showed visitors a corresponding pop-up window.
In yet another case, the malicious file Login_Details.img was also distributed from the site ecb-european[.]eu. Our colleagues from Group-IB have provided a detailed analysis of the malware.
2. Malicious VHD
In late December 2019, we detected another CobInt loader used by Cobalt. The loader container was unusual. It was a virtual hard disk (VHD), presumably distributed by email.
The VHD format was originally developed by Connectix for their Virtual PC product. Microsoft acquired the product in 2003 and renamed it Microsoft Virtual PC. In 2005, the format became available to the public. Microsoft started using the VHD format in Hyper-V, the hypervisor-based virtualization technology. A VHD file may contain anything found on a physical hard drive, such as disk partitions and a file system with folders and files.
Windows 7 and newer systems include the ability to manually mount VHD files, such as via the MMC console. Starting with Windows 8, a user can mount a VHD by simply double-clicking the file. A mounted VHD disk image appears to Windows just like a normal hard disk.
In September 2019, the CERT/CC Blog published an article about the danger of VHD files and their possible use as an attack vector. The researcher Will Dorman showed that neither antivirus software nor the Mark of the Web alerts users about the potential harm of the contents of a VHD file downloaded from the Internet. Dorman created a malicious VHD container with EICAR inside and uploaded the result to VirusTotal. The malware was not detected by any antivirus engines. A VHD file is critical for operation of Hyper-V virtual machines. If this file is damaged or blocked, the virtual machine will not run. This may explain the rarity, or even absence, of antivirus detection. In documentation, Microsoft recommends excluding VHD files from antivirus scanning (as automatically is the case in Windows Defender). Otherwise, Hyper-V is susceptible to issues.
It is possible that Cobalt used the findings of this research for their own purposes. Their VHD file was also not detected by any antivirus software when it first appeared on VirusTotal. Half a year later, the file was detected by just one antivirus engine, which is still very low.
The VHD contains two CobInt files. One file has two invalid Google certificates appended to it in order to reduce the odds of detection.
Since VHD is in essence a container with a file system, one can search for artifacts inside VHD files. For example, we found an image with text of a fake HSBC antifraud message in the unallocated space of a VHD file.
The attackers may have inadvertently left the artifact when reallocating space in the container: the same image was used as the CobInt icon and stored in the group's resources.
2.1. CobInt analysis
Once the VHD is mounted, a user must manually run one of the files. The two files are identical in terms of functions. When run, either of the CobInt files downloads the main library from the C2 server as an HTML file.
There are a few changes in comparison to the algorithm described by ProofPoint in 2018:
First, all tags are removed and their contents are ignored.
Next, periods, commas, and spaces are processed. All characters after these symbols are uppercased (the value 0x20 is subtracted).
Next, data is decoded from Base64 and decrypted by XOR with a 4-byte key that is initialized with the preceding value of the decrypted data at each iteration. At each iteration, the current round's 4 bytes are subtracted from those of the previous round, after which the key is the 4-byte value of the input buffer of the previous round.
Once decryption finishes, the second-stage decryptor takes over. In essence, it consists of an XOR decryption cycle using a 4-byte key that is the same for the entire stage. The output of this stage will be a .dll library, which is the payload.
Data decoded from Base64 is shown in Figure 16. A 4-byte preset for the first decryptor is highlighted in red and will remain the same during the second stage. The rest of the data is highlighted in yellow.
The picture changes after decryption (Figure 17). The encryption key is clearly visible due to a long series of zeros in the executable file that, after encryption, contain the keystream in pure form.
The second decryption gives us a valid PE file (Figure 18). We could not figure out the purpose of the first eight bytes: they are not used anywhere in the loader.
2.2. Main library analysis
Once an event is created and the necessary parameters are initialized, the domain is decrypted. Then the function for generating the remaining part of the address is called.
After the full C2 server address is generated, the library decrypts the necessary parameters to create HTTP fields, adds them to a request, and sends the request to the server. The server response contains plugins that the library loads into its address space using ReflectiveLoader.
2.3. Decryption of plugins
Like the main library, the plugins are sent by the server as HTML pages. The first stage of input transformation is similar to what happens during the library download. The difference is that all periods, commas, and spaces are ignored, and all characters are lowercased.
After the initial transformation, the obtained data is decoded from a–z to 0x00—0xff. For this, a previously unseen decoding procedure is used. It is based on transforming input values depending on the current value, previous value (in some cases), and values of the global counter.
The decoding is followed by two decryption cycles.
The first decryption key is in the application code, hard-coded at an offset that takes only two values.
To carry out the second decryption cycle, the last byte of data is read. This byte is the length of the encryption key for the second cycle. The file is read at this number of bytes (plus one) from the end. After the key is read, the data is decrypted, except for the key itself. In Figure 22, the key length is highlighted in red and the key itself is highlighted in yellow.
The last stage is decryption with a 4-byte key, which is also easily obtained by analyzing the series of zeros in the PE header.
Our analysis detected two types of downloaded plugins: one that steals the names of running processes plus a screen capture module. Both plugins use standard WinAPIs to obtain data, as well as the same function as the main library in the export for reflective process loading.
2.4. Traffic decryption
The library sends the data collected by the plugins to the server.
Here is an example of traffic:
First, the traffic is encrypted by a randomly generated key of arbitrary length. The key is inserted into the packet with indication of its length.
Next, the data is encrypted with a hard-coded 64-byte key, the same one that was used to decrypt the library. After that, the same encoding algorithm is applied. An equals sign (=) and the generated sequence of 8 to 16 a–z characters are added to the beginning.
Example of decoded and decrypted traffic from the previous packet:
Packet creation and transformation function:
3. BIFF macro
In March 2020, we detected an XLS document from Cobalt that downloaded and ran the COM-DLL-Dropper. The document contained the rather old Excel 4.0 macro format and was almost invisible to antivirus software (1 positive verdict out of 60 on VirusTotal).
This macro standard is 20 years old. The standard is peculiar in that the macro is stored in worksheet cells (not stored in a VBA project), and the worksheet itself can be hidden in Excel. The macro therefore will not be in a VBA stream, but in a BIFF (Binary Interchange File Format) record.
If we open the document in Excel, we see one worksheet and no VBA project macros. However, Excel all the same detects the macro and blocks it from running.
The olevba3.py utility from oletools can be used to detect this macro.
By running the utility, we see that one of the document worksheets has the status "very hidden" and is of the Excel 4.0 macro type. Because of this status, the worksheet will be invisible in the Excel interface and, what's more, it cannot be made visible from the interface either. It can only be made visible by Visual Basic or by manually modifying the document's bytes.
The BiffView utility provides a more workable view of the BIFF structure. After parsing the initial document, we see that a page named sygfdfdfdesie has the attribute "very hidden." We change this parameter to 1 or 0 in a hex editor.
When the initial document is opened in the Name Manager, one of the formulas runs automatically:
The initial formula launches a long chain of commands, such as CONCATENATE, RUN, CHAR, and CALL, which will lead to the loading and launch of COM-DLL-Dropper. The commands are scattered across the Excel cells, complicating analysis.
4. COM-DLL-Dropper analysis
In early April 2020, we detected a new version of COM-DLL-Dropper. Its functions are different from everything we had seen before. However, the more_eggs JavaScript backdoor payload remained the same.
Cobalt first started using COM-DLL-Dropper in the summer of 2017 and is still using it to deliver more_eggs, which is contained in the dropper in encrypted and archived form.
A few facts about the dropper:
- It is written completely in PureBasic.
- It uses numerous anti-analysis techniques.
- It contains an encrypted and archived JavaScript loader, JavaScript backdoor, and a legitimate utility for modifying the command line to launch more_eggs.
- It has a built-in obfuscator for the hard-coded JavaScript backdoor and JavaScript loader
4.1. PE file external structure
All the studied items are PE-DLL files to be registered by regsvr32. In addition to exports called by regsvr32, each sample has different sets of exports typical of legitimate DLL files. Cobalt attempted to mask COM-DLL-Dropper by using third-party exports. Figure 32 shows the most popular exports used in the malware files (total of 249 unique exports).
These exports contain stubs that generally do not actually do anything. Judging by the names of the exports, the droppers were masked to resemble media application libraries. In 2019, the malware was updated with the DllInstall export, which is also called by regsvr32, and the main dropper code was moved to the export.
Before describing the malware code, we should touch on the PureBasic code. The information we provide here is the result of analyzing malware samples. We did not study the compiler itself and therefore are forced to make certain assumptions. However, the described entities helped us in our analysis, which is why we are sharing them here. All the names in screenshots were made up for the purposes of interpreting the malware code.
Our analysis requires two entities: strings and object arrays. PureBasic strings are stored in a special buffer. They are allocated and released without using a system API. Figure 33 shows the process of string allocation. During program initialization, a separate heap is created for strings by calling HeapCreate().
A common pattern for working with this entity is as follows:
- Allocate a string to storage from a constant.
- Operate on the string.
- Update the global string which is usually allocated on the heap. After the update is completed, move back the node index. This operation is somewhat similar to pop().
The string storage structures do not allow storing the size of the added string. Instead, before starting any operation with the string, the program saves the previous index of the node and then passes it to the update operation. The difference between the indices is the string size.
We will not describe object arrays here in detail; suffice it to say that a special header before each array stores information about the size, type, and number of elements. The header occupies 18h bytes. Therefore, the space allocated for the array of objects can be calculated as size of element × number of elements + 18h.
To get a clearer picture, refer to this description of functions that are presented in the screenshots a bit later.
Table 1 Function Description
Function | Description |
---|---|
ObjectManager::AllocateObjectArray |
Object array is allocated |
ObjectArray::ReleaseObjectArray ObjectManager::FreeObject |
Object array is released |
ObjectManager::GetStringObject ObjectManager::ConcatenationWithStringObject |
Create a string in storage |
ObjectManager::PopStringObject |
Update global string |
4.2. Anti-analysis
To find the needed API functions, non-standard hash sums obtained from the functions' names are used. Each hash sum is obtained by taking the CRC32 value and then performing XOR with a constant. The samples have different constants. This is why Table 3 also includes CRC32 values without the constant-value XOR.
The new version of COM-DLL-Dropper has strings encrypted with the RC4 algorithm, whereas the older version used XOR.
Table 2. Techniques used by the malware to complicate analysis
Technique | Description |
---|---|
Key bruteforce to decrypt strings |
Starting in April 2020, RC4 has been used instead of XOR. This technique uses a non-standard implementation of the Sleep function, which may postpone launch of the main malware functions in a sandbox. |
Checking for the /s /i string process in CommandLine |
The check verifies that the process was launched via regsvr32. |
Verifying the process name and the .ocx extension |
The extension and the process name are also checked with a non-standard hash function. |
Verifying the list of modules loaded into the process |
The check is performed using a custom hash function. |
Loading of additional NTDLL image into the process |
This likely creates a trusted NTDLL image without NT API interception. |
Checking the values of registers Dr0–Dr3 |
Non-zero values in these registers indicate hardware breakpoints, and therefore the debugger. The register values are accessed via NtGetContext(). |
ProcessDebugPort check |
NtQueryInformationProcess with relevant value is called. |
ProcessDebugObjectHandle check |
NtQueryInformationProcess with relevant value is called. |
ProcessDebugFlags check |
NtQueryInformationProcess with relevant value is called. |
Checking the parent process name |
The check is performed using a non-standard hash function. |
Checking the year set on the system |
The current date is obtained by calling NtQuerySystemTime and RtlTimeToTimeFields. |
Checking the value of the environment variable COMPUTERNAME |
The computer name is checked for the hard-coded string "FLAREVM". |
Table 3. Strings and corresponding hash sums used in techniques
Hash sum | CRC32 | String |
---|---|---|
0x322CD34E |
0x322C4A66 |
.ocx |
0xF43AEA50 |
0xF43A7378 |
regsvr32.exe |
0x6FECDEE9 |
0x6FEC47C1 |
sbiedll.dll |
0x16430EDF |
0x164397F7 |
cmdvrt64.dll |
0x2B256AC8 |
0x2B25F3E0 |
cmd.exe |
0xA82757CC |
0xA827CEE4 |
cmstp.exe |
0xB3C6B186 |
0xB3C628AE |
msxsl.exe |
Key bruteforcing for string decryption is not "complete." In fact, most of the key consists of a hard-coded prefix found in the code. The end of the key is a decimal number. Therefore, key = prefix + number.
4.3. JavaScript generators
The dropper creates two files. The first is a JavaScript loader, and the second is a scriptlet containing the encrypted more_eggs backdoor. Both scripts are generated.
The generation template is saved among malware samples. The inserted data varies. The template contains tokens and JavaScript parts that are concatenated in series. Figure 35 shows part of generation of the JavaScript loader and examples of the used JavaScript parts.
Several obfuscation templates are built into the generator:
- Compensatory disguising of constants
- Generation of random variable names
- Insertion of encrypted strings
Each generator contains a pool of names that are generated prior to starting creation of the script. These names are then used in JavaScript. Figure 35 shows a local variable named lpbobj_arraywcs__JSObjectPool. Figure 36 shows the pool initialization cycle.
Each name available to be used in the script contains two parts: a random prefix (which is created once for the entire script) and a random decimal number (limited to a set number of characters). Figure 37 shows the name generation scheme and the result obtained in the script.
Numeric constants are obfuscated with a function that applies a random arithmetic operation from a set hard-coded in the program and then inserts the opposite operation in the script. Thus, the inserted expression balances out the obfuscated constant. The second arithmetic operation argument is also generated randomly from a hard-coded range of values.
The payload is encoded using RC4 and Base91 and inserted in the script. The implementations of RC4 and the Base91 decoder are also inserted in the scripts.
4.4. Persistence
Depending on its rights in the system, the dropper entrenches itself on the infected machine using the following methods:
- By using Task Scheduler
- By using the registry key Environment\UserInitMprLogonScript
- By using the registry key Software\Microsoft\Windows\CurrentVersion\Run
For all three methods, the value written by the dropper is the same, containing the command for launching the JavaScript loader.
To configure a task created by the dropper, a special XML file is generated. Part of it is stored in the dropper in encrypted form, and another part is generated while running.
The resulting XML file is saved with a random name consisting of hexadecimal characters. Subsequently, this XML file is passed to schtasks.exe as the /XML parameter value.
4.5. Running the payload
COM-DLL-Dropper saves three files to disk:
- Obfuscated JavaScript loader
- Obfuscated JavaScript backdoor
- Legitimate utility for modifying the command line in order to launch the more_eggs JavaScript backdoor
The main backdoor is launched with the help of a known AppLocker bypass technique using the msxsl utility. The commands look as follows:
- “C:\Users\\AppData\Roaming\Microsoft\msxsl.exe”
- “C:\Users\\AppData\Roaming\Microsoft\[javascript_downloader_name].txt”
- “C:\Users\\AppData\Roaming\Microsoft\[javascript_backdoor_name].txt”
4.6. JavaScript backdoor functionality
The JavaScript backdoor saved to disk by the new COM-DLL-Dropper has version 6.6.
This backdoor has been used by Cobalt since 2017. It is executed in memory and always has a low number of antivirus verdicts.
The main capabilities of the backdoor are as follows:
- Traffic encryption with RC4 and Base91
- Execution of operator commands (in this version, the more_eggs command that gave the backdoor its name was absent):
- exec: download and run file (.exe or .dll)
- gtfo: uninstall
- more_onion: run script
- via_c: execute command using "cmd.exe /C"
- more_time: execute command using "cmd.exe /C", with the result being saved to a temporary file. After that, the file is read and deleted, and its contents are encoded with Base64 and sent to the server.
- Check of the process list for antivirus protection and researcher software by comparing CRC32 values (derived from the name of each process, without extension and in lower case) against hard-coded values.
- Reconnaissance:
- Date of system installation
- Infected machine's IP address
- System type (server or desktop)
- Windows version (from XP to 10)
Conclusion
Cobalt keeps attacking financial organizations around the world, refining its TTPs, and inventing ever-more sophisticated ways to bypass defenses. Due to quarantine-related measures, many employees of financial companies are now working remotely, outside the protection offered by corporate security solutions. Moreover, many threat actors are using COVID-19 as a lure in their attacks, as the Higaisa group has done. It is possible that Cobalt, too, will try to weaponize such concern.
Authors: Denis Kuvshinov, Sergey Tarasov, Daniil Koloskov, PT ESC
Indicators of compromise
ECB phishing
Type | MD5 | SHA1 | SHA256 |
---|---|---|---|
Browser droppers |
152cd7014811ae8980981a825e5843b0 |
90f7d0b0f90aeadaeff1adf45db5dcc598dec8c4 |
2d02bbae38f4dba5485fbc2e38640898907ecdd6b9ee43501d1ee951653ab36f |
|
f2712de0c8575ff32828c83cfbf75d4b |
e80ef396462fe651c3cdeb91651ac27799d2dab5 |
33ba8cd251512f90b7249930aee22d3f47255420a8d41e1326169e0f948cc7d0 |
|
a3391d1d3482553545d7c0111984abb6 |
1a371353c6a46ddea19d520d8ce3b5599a8ee9f1 |
9e8a99ad401ef5d2bb3aea3a463d85220f0e6724f91a3c2ffd195d0b8628bf9d |
CobInt |
f924c690f7bbaf60d56a446b7a66a43b |
8ada87f00ed3afdd4dbdb07879ba6ebe4a2a9ffa |
b83d2c4f5c2bb562981a104d4e49cf25291096d37a4161c32a76e369d1a931e8 |
C&C
ecb-european[.]eu
timeswindows[.]com
VHD
Type | MD5 | SHA1 | SHA256 |
---|---|---|---|
VHD-file |
fce9fcd5fa337d020bd6758008221b81 |
e288b0410fb95060ce8c5527673978cb2ceffe05 |
3382a75bd959d2194c4b1a8885df93e8770f4ebaeaff441a5180ceadf1656cd9 |
CobInt |
600154fcb03e775f007ef7b1547b169c |
384a13abe42d249e354cd415c4bcbf01086deafb |
0c85c1045899291cba47c7171599446642b87015a76d5b22f8cc51f4a6e45a90 |
|
6ec0edd1889897ff9b4673600f40f92f |
4d50f1cae2acc8c92ff1f678fc1fdfdd1e770f24 |
64d16900fce924da101744edce28b9ee648192486d9062c427c17589b5f204fb |
C&C
telekom-support[.]info
45.80.69[.]34
BIFF
Type | MD5 | SHA1 | SHA256 |
---|---|---|---|
XLS-File |
36399ebf94f66529dc72d8b2844f43dd |
b912f222e79feadbcefe2d6ead5fab74b15b1f40 |
0aee265a022ee84e9c8b653e960559c9761a7362e1c345019a552188114b7e80 |
COM-DLL |
862c19b2b4b6a7c97fb8627303b8f5d7 |
d3fc5f848d630ca2dc8e99b0d4dfe704b8ec1832 |
7122cf59f8a59f9a44f20fd4c83451c5c4313e0021d3f1ba9c2b1a4f39801db1 |
C&C
download.sabaloo[.]com
origin.cdn77[.]kz
New COM-DLL dropper
Type | MD5 | SHA1 | SHA256 |
---|---|---|---|
COM-DLL |
47e7212b097b5cffa60903055e3c4d5a |
dfcd5692729e859f074b95720505f711ba7d14ac |
c1a633a940fc4c595ebbe36823fee1b02bfd755615c51799c9f4e4320b597af1 |
С&C
maps.doaglas[.]com
MITRE TTPs
Tactic | ID | Name |
---|---|---|
Initial Access |
T1193 |
Spearphishing Attachment |
|
T1192 |
Spearphishing Link |
Execution |
T1059 |
Command-Line Interface |
|
T1117 |
Regsvr32 |
|
T1204 |
User Execution |
|
T1064 |
Scripting |
Persistence |
T1037 |
Logon Scripts |
|
T1050 |
New Service |
|
T1053 |
Scheduled Task |
|
T1060 |
Registry Run Keys / Startup Folder |
Defense Evasion |
T1027 |
Obfuscated Files or Information |
|
T1220 |
XSL Script Processing |
Discovery |
T1063 |
Security Software Discovery |
Command And Control |
T1105 |
Remote File Copy |