We have noticed 2 changes since we published this report 3 months ago.
- The code has been adapted to use registry key “HKEY_CURRENT_USER\SOFTWARE\Microsoft\Personalization” instead of “HKEY_CURRENT_USER\SOFTWARE\Microsoft\Phone” (sample SHA256 ed2f654b5c5e8c05c27457876f3855e51d89c5f946c8aefecca7f110a6276a6e)
- When the payload is Cobalt Strike, the beacon configuration now contains hostnames for the C2, like r1dark[.]ssndob[.]cn[.]com and r2dark[.]ssndob[.]cn[.]com (all prior CS samples we analyzed use IPv4 addresses).
In this blog post, we will perform a deep analysis into GootLoader, malware which is known to deliver several types of payloads, such as Kronos trojan, REvil, IcedID, GootKit payloads and in this case Cobalt Strike.
TLDR techniques we used to analyze this GootLoader script:
Several new functions were added to the original jQuery script. Analyzing these functions would show a blob of obfuscated data and functions to deobfuscate this blob.
- The algorithm used for deobfuscating this blob (trojan downloader):
- For each character in the obfuscated data, assess whether it is at an even or uneven position (index starting at 0)
- If uneven, put it in front of an accumulator string
- If even, put it at the back of the accumulator string
- This failed due to the payload not being served anymore and we resorted to make an educated guess to search for an obfuscated (as defined in the previous output) “createobject” string on VirusTotal with the “content” filter, which resulted in a few hits.
Stage 2: Decode the obfuscated payload
- Take 2 digits
- Convert these 2 decimal digits to an integer
- Add 30
- Convert to ASCII
- Repeat till the end
- Stage 3: Analyze the .NET loader to deobfuscate the Cobalt Strike DLL
- Stage 4: Extract the config from the Cobalt Strike DLL
Stage 1 – sample_supplier_quality_agreement 33187.js
Filename: sample_supplier_quality_agreement 33187.js
File Size: 287 KB
grep -P "^[a-zA-Z0-9]+\("
When tracing the different calls, there’s a lot of obfuscated code, the function “color1” is observed Another way to figure out what was changed in the script could be to compare it to the legitimate version of the script and “diff” them to see the difference. The legitimate script was pulled from the jQuery website itself, based on the version displayed in the beginning of the malicious script.
Before starting a full diff on the entire jQuery file, we first extracted the functions names with the following grep command:
grep 'function [0-9a-zA-Z]'
This was done for both the legitimate jQuery file and the malicious one and allows us to quickly see which additional functions were added by the malware creator. Comparing these two files immediately show some interesting function names and parameters:
A diff on both files without only focusing on the function names gave us all the added code by the malware author.
Color1 is one of the added functions containing most of the data, seemingly obfuscated, which could indicated this is the most relevant function.
The has6 variable is of interest in this function, as it combines all the previously defined variables into 1:
Further tracing of the functions eventually leads to the main functions that are responsible for deobfuscating this data: “modern00” and “gun6”
The deobfuscation algorithm is straightforward:
For each character in the obfuscated string (starting with the first character), add this character to an accumulator string (initially empty). If the character is at an uneven position (index starting from 0), put it in front of the accumulator, otherwise put it at the back. When all characters have been processed, the accumulator will contain the deobfuscated string.
The script used to implement the algorithm would look similar to the following written in Python:
This script is a downloader script, attempting to initiate a download from 3 domains.
The HTTPS requests have a random component and can convey a small piece of information: if the request ends with “4173581”, then the request originates from a Windows machine that is a domain member (the script determines this by checking for the presence of environment variable %USERDNSDOMAIN%).
The following is an example of a URL:
Stage 2 – Payload
We were able to find a payload that we believe, with high confidence, to be the original stage 2. With high confidence, it was determined that this is indeed the payload that was served to the infected machine, more information on how this was determined can be found in the following sections. The payload, originally uploaded from Germany, can be found here: https://www.virustotal.com/gui/file/f8857afd249818613161b3642f22c77712cc29f30a6993ab68351af05ae14c0f
File Size: 1012.97 KB
The payload consists of digits. To decode it, take 2 digits, add 30, convert to an ASCII character, and repeat this till the end of the payload. This deobfuscation algorithm was deduced from the previous script, in the last step:
As an example, we’ll decode the first characters of the strings in detail: 88678402
- 88 –> 88+30 = 118
- 67 –> 67 + 30 = 97
- 84 –> 84 + 30 = 114
- 02 –> 02+30 = 32
To decode the entire string a bit faster we can use a small Python script, which will automate the process for us:
First half of the decoded string:
Second half of the decoded string:
The same can be done with the following CyberChef recipe, it will take some time, due to the amount of data, but we saw it as a small challenge to use CyberChef to do the same.
File size: 507 KB
- powershell_loader: this PowerShell script is a loader to execute the PE file injected into the registry
- powershell_persistence: this PowerShell script creates a scheduled task to execute the loader PowerShell script (powershell_loader) at boot time.
A custom script was utilized to decode this payload as a whole and extract all separate elements from it (based on the reverse engineering of the script itself). The following is the output of the custom script:
All the artifacts extracted with this script match exactly with the artifacts recovered from the infected machine. These can be verified with the fileless artifacts extracted from Defender logs, with matching cryptographic hash:
- Stage 2 SHA256 Script: 12c0067a15a0e73950f68666dafddf8a555480c5a51fd50c6c3947f924ec2fb4
- Stage 2 SHA256 Persistence PowerShell script (powershell_persistence): 48e94b62cce8a8ce631c831c279dc57ecc53c8436b00e70495d8cc69b6d9d097
- Stage 2 SHA256 PowerShell script (powershell_loader) contained in Persistence PowerShell script: c8a3ce2362e93c7c7dc13597eb44402a5d9f5757ce36ddabac8a2f38af9b3f4c
- Stage 3 SHA256 Assembly: f1b33735dfd1007ce9174fdb0ba17bd4a36eee45fadcda49c71d7e86e3d4a434
- Stage 4 SHA256 DLL: 63bf85c27e048cf7f243177531b9f4b1a3cb679a41a6cc8964d6d195d869093e
Based on this information, it can be concluded, with high confidence, that the payload found on VirusTotal is identical to the one downloaded by the infected machine: all hashes match with the artifacts from the infected machine.
In addition to the evidence these matching hashes bring, the stage 2 payload file also ends with the following string (this is not part of the encoded script): @[email protected] This is the random part of the URL used to request this payload. Notice that it ends with 4173581, the unique number for domain joined machines found in the trojanized jQuery script.
Payload retrieval from VirusTotal
Although VirusTotal has reports for several URLs used by this malicious script, none of the reports contained a link to the actual downloaded content. However, using the following query: content:”378471678671496876716986″, the download content (payload) was found on VirusTotal; This string of digits corresponds to the encoding of string “CreateObject”. (see Fig. 20)
In order to attempt the retrieval of the downloaded content, an educated guess was made that the downloaded payload would contain calls to function CreateObject, because such functions calls are also present in the trojanized jQuery script. There are countless files on VirusTotal that contain the string “CreateObject”, but in this particular case, it is encoded with an encoding specific to GootLoader. Each letter of the string “CreateObject” is encoded to its numerical representation (ASCII code), and subtracted with 30. This returns the string “378471678671496876716986”.
Stage 3 – .NET Loader
MD5 Assembly: d401dc350aff1e3fd4cc483238208b43
SHA256 Assembly: f1b33735dfd1007ce9174fdb0ba17bd4a36eee45fadcda49c71d7e86e3d4a434
File Size: 13.50 KB
This .NET loader is fileless and thus has no filename.
The PowerShell loader script (powershell_loader)
- extracts the .NET Loader from the registry
- decodes it
- dynamically loads & executes it (i.e., it is not written to disk).
The .NET Loader is encoded in hexadecimal and stored inside the registry. It is slightly obfuscated: character # has to be replaced with 1000.
The .NET loader:
- extracts the DLL (stage 4) from the registry
- decodes it
- dynamically loads & executes it ( i.e., it is not written to disk).
The DLL is encoded in hexadecimal, but with an alternative character set. This is translated to regular hexadecimal via the following table:
This Test function decodes the DLL and executes it in memory. Note that without the .NET loader, statistical analysis could reveal the DLL as well. A blog post, written by our colleague Didier Stevens on how to decode a payload by performing statistical analysis can offer some insights on how this could be done.
Stage 4 – Cobalt Strike DLL
MD5 DLL: 92a271eb76a0db06c94688940bc4442b
SHA256 DLL: 63bf85c27e048cf7f243177531b9f4b1a3cb679a41a6cc8964d6d195d869093e
This is a typical Cobalt Strike beacon and has the following configuration (extracted with 1768.py)
Now that Cobalt Strike is loaded as final part of the infection chain, the attacker has control over the infected machine and can start his reconnaissance from this machine or make use of the post-exploitation functionality in Cobalt Strike, e.g. download/upload files, log keystrokes, take screenshots, …
About the authors
|Didier Stevens||Didier Stevens is a malware expert working for NVISO. Didier is a SANS Internet Storm Center senior handler and Microsoft MVP, and has developed numerous popular tools to assist with malware analysis. You can find Didier on Twitter and LinkedIn.|
|Sasja Reynaert||Sasja Reynaert is a forensic analyst working for NVISO. Sasja is a GIAC Certified Incident Handler, Forensics Examiner & Analyst (GCIH, GCFE, GCFA). You can find Sasja on LinkedIn.|
You can follow NVISO Labs on Twitter to stay up to date on all our future research and publications.