Ransomware written in the Go language is quickly becoming more popular among threat actors. These include Babuk, Hive, and HelloKitty, as well as many other threats written in Golang. “Go” is a statically typed, object-oriented, cross-platform programming language introduced by Google. The abstraction and the support for multiple platforms is an advantage for many developers and also a disadvantage for security vendors who attempt to create signatures for malicious executable malware, which comes with all the dependent libraries built-in.
Morphisec Labs has identified a new strain of ransomware, implemented in Go 1.17 and named DECAF. The first version, which includes symbols and test assertion, was identified in late September. The attackers very quickly stripped the original alpha version, added additional functionality, and uploaded this stub version to verify its detection score. Within a week they had deployed a fully weaponized version on a customer site.
Golang 1.17 introduces additional complexity to analyze the application flow due to a modification in how parameters are being passed to functions, this is a great example of how the attackers are becoming extremely agile in utilizing the latest technology.
The blog post that follows will cover in great detail the different debug and pre-release versions of the new ransomware strain, as well as how the threat actor successfully encrypts their target.
As has been described in the introduction, we have identified the delivery of the DECAF ransomware on one of our customer’s sites. It is only following a detailed investigation that we successfully found a trail that leads us to a debug version of the ransomware, which also included symbols. In the first technical part, we will go into great detail about the functionality of this debug version step by step. In the second part of the blog, we will identify the updates introduced to the pre-release version. We are aware of more updated versions that have been deployed during the last two weeks.
Figure 1: The attack chain of the Golang ransomware
The initialization phase sets up the data required for the ransomware’s malicious activity.
The malware starts by parsing a command-line argument, --path, which represents the root directory where the ransomware will start recursively encrypting files.
Figure 2: Parsing --path parameter
Next, the malware creates an Encryptor object structure:
Figure 3: Initializing the encrypter with relevant data
Many ransomwares implement file filtering mechanisms for several purposes. Controls to avoid double encrypting the same file and avoiding the wrecking of the victim’s operating system for payment.
DECAF is no different and also uses a files filtering mechanism. It ignores:
For that task, the attacker created a FileUtils class which has a pointer to README.txt string (the name of the ransomware notification file) and the relevant functions. One of the functions inside FileUtils is Init(). This function is responsible for building blacklists for files, folders, and file extensions (the list’s content can be found in the Appendix section).
The next step is figuring out which directories the malware should encrypt. It checks if --path has value and if not calls to FileUtils.ListDriverRootPaths()as shown in the figure above.
Looking inside ListDriverRootPaths, we can see that the malware iterates over the possible drives and searches for drives with a type that is NOT a DRIVE_CDROM.
Figure 6: Adding drives excluding DRIVE_CDROM type to the slice
The last thing that the malware does in this phase is to create a WMI object for future use. We’ll go over its functionality when we show the mechanism used to delete files.
The encryption phase starts by adding the attacker’s email into the ransom note.
Figure 7: Creating the formatted ransom note string
As you may know, one of the biggest challenges ransomware authors face when developing ransomware is encryption performance. The malware needs to encrypt as many files as possible, as fast as possible.
The author of DECAF chose the multi-goroutine (Go’s thread “equivalent”) method. It creates several encryption goroutines which wait for messages from the main routine. The message contains the file path that it has to encrypt.
Figure 8: Creating a communication channel and 4 Go Routines
Each EncWorker waits to receive a new file path to encrypt from the channel. The file paths come from the function FileUtils.ListFilesToEnc, which enumerates the files of the given directory and applies filtering according to the blacklists, README.txt, and .decaf extension.
Figure 9: The main goroutine sends file paths after filtering and skipping symlinks
Figure 10: check arg_file_path for symlink
main_EncWorker_func1 is the function responsible for the encryption task. It listens for new file paths, calls the file encryption function, deletes the original file after it is encrypted, and creates a README.txt file inside each directory.
Figure 11: main_EncWorker_func1 functionality
Once the file path has been received, the function calls Encryptor.E for encrypting the file.
The encryption routine is as follows:
ciphertext, err := EncryptOAEP(sha256.New(), aes_key, public_key, G_Reader, 0x10) |
The next thing is to open the source (original file) and target (encrypted file) files. The malware opens the original file with OF_READWRITE permission and creates a new target file with .decaf extension.
Figure 14: Open original and target files
The attacker needs to be able to decrypt all files in case someone pays the demanded ransom to maintain its credibility. To do that, the attacker creates a special header for each file that contains the relevant data for decryption.
Encrypted file format:
{ CBC_IV // Shared between encryption and decryption |
<code: go>
Figure 15: Encrypted file format
The file content is divided into chunks, where each chunk is 0x10 bytes. We wrote simple pseudocode which represents the content encryption’s logic:
func EncryptFileContent() padding := make([]byte, padLen) |
We can assume that the author chose to divide the data into such small chunks as a way to evade detection by Anti-Ransomware solutions that monitor for large data chunk encryptions.
Once the ransomware has created the encrypted file it needs to delete the original file and eliminate the target’s ability to recover the file.
First, the malware deletes the file using the WMI object created in the initialization phase. We’ve reconstructed the malware’s WMI usage in the following pseudocode:
func DeleteFileUsingWMI() { unknown, _ := oleutil.CreateObject("WbemScripting.SWbemLocator") wmi, _ := unknown.QueryInterface(ole.IID_IDispatch) service := serviceRaw.ToIDispatch()
itemRaw, _ := oleutil.CallMethod(result, "ItemIndex", i) item := itemRaw.ToIDispatch() |
Now the last thing left is to remove the recovery ability on the infected system. For that, DECAF utilizes cipher.exe, similarly to other ransomware (e.g., LockerGoga and MegaCortex).
DECAF iterates over the directories it needs to encrypt and calls cipher.exe with a /w:<directory_path>. This option overwrites (“wipes”) deleted data and, as a result, eliminates the ability to recover the file.
Figure 16: Wiping delete data inside the directory
The difference between the two versions of the same ransomware is that the pre-release variant is stripped of symbols, strings and function names are obfuscated.
We assumed that the second version is a Pre-Released version due to the Protonmail used in the ransom note, which is filled with a placeholder instead of a real email address.
Figure 17: Email address in the ransom note of the Pre-Release version
Let’s take a look at the code from runtime.schedinit that contains the variable buildVersion. This variable points to the Golang version that has been used, at least in the case that the symbols are present and not stripped.
It’s worth mentioning that Go 1.17 implements a new way of passing function arguments and results using registers instead of the stack. Because of this, reverse engineering Golang could become messy for newcomers.
https://golang.org/doc/go1.17#compiler
The ransomware uses string obfuscation in its Pre-Release version. Strings are being de-obfuscated on runtime while utilizing different custom de-obfuscation functions.
For example, the initialization of the `Encryptor` object’s decaf extension attribute:
Figure 20: .decaf extension deobfuscation
Another example could be seen while deleting the original file. The WMI query used in the Debug version was embedded into the binary while in the Pre-Release version it was stored encrypted. Before calling the delete function, the malware executes the decryption function and reveals the real WMI query, as we saw in the Debug version.
Figure 21: WMI query resolving
The development of DECAF continues to this day, showing that ransomware groups constantly innovate their attacks. That the attack is written in Golang is further proof of this trend toward innovation among the adversary community; threat actors are forever making changes and adding new capabilities to evade the detection-centric solutions that predominate in the market.
Companies need to adopt prevention-first strategies, such as the ones Morphisec provides, to ensure that they stand a chance at protecting their critical systems from further attacks. Morphisec Labs will continue to track the development of the DECAF ransomware and report any further developments that we uncover.
-----BEGIN RSA PUBLIC KEY----- |
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAq4k1Hdb1THrzBBeO184knCbBKr03apfXqlOkSdtHSJgfyIqJPGxl /cFisJmVXR3/t4e9FbLsEIuTp9PJTciomHfr5CgCQzhnAZ0AvjGBaWP6KpCyfDns ybruyKqygaWpZSAnzRdB+TAku5iqy8q1VwnN57QBltro0YJZ8enKZRTlczmtjeOp B/xuTOuDjmUSNiGyijWBVfYk7sVXl/lQ8taXr36xPWhMIG0EqRVrFV+cavS7Z4va yXmcf55NkpMGKKY8uqvwb4aLIKabek2nUWBgNgSOtqBLLL2A2bY/5s0GJ/VV+EmI X7/zI+FceU+dcNX/ir0ujP4ys4m/jjZD4wIDAQAB -----END RSA PUBLIC KEY----- |
5da2a2ebe9959e6ac21683a8950055309eb34544962c02ed564e0deaf83c9477
98272cada9caf84c31d70fdc3705e95ef73cb4a5c507e2cf3caee1893a7a6f63
bootfont.bin |
intel |
.themepack .shs .prf |