Lockkey is a ransomware variant written in the Go programming language, making it potentially more cross-platform and resilient than ransomware traditionally written in languages like C++. While the specifics of its technical mechanisms are unavailable due to the restricted source, here’s a breakdown of common ransomware behaviors and potential areas of analysis:
SHA-256: eb58cbfca307a9d3cfe718d772f7a53079db87bc8936023d6b7adb8cf7206711
Upon the completion of the preliminary automated analysis, our attention is drawn to a pivotal function named main_main. Immediately following our arrival at the initial graph within the main function, we encounter the code’s opening segment.
This segment is tasked with the crucial setup of the stack frame and the initialization of local variables. It also orchestrates the invocation of GO runtime functions, which are integral to the program’s operation. This setup phase culminates with the insertion of a nop instruction, a step that is typically employed for the purposes of either alignment or padding within the code.
In the subsequent segment of graph, we observe that the code undertakes the task of loading a specific data structure. This structure is populated with a catalog of processes designated for termination. Post-loading, the code initiates a call to an additional function, aptly named enc_pkill_Pkill. Prior to drawing any conclusions regarding the function’s operation, it is imperative that we conduct a thorough examination of the code.
At the outset, the code initiates a loop that iterates over the array of processes. These processes are passed to it as an argument, originating from the antecedent data structure that enumerates the processes slated for review. This looping mechanism is the first operational step in the function’s execution sequence.
Subsequently, the code proceeds to append the .exe extension to the process names retrieved from the earlier mentioned data structure. This action is conditional and is executed only if the process names are initially devoid of the .exe suffix. This ensures that all process names conform to the required format for further operations.
Continuing its operation, the code appends the taskkill /im /f command to the process names. This command is a forceful directive to the taskkill system utility, compelling it to terminate the specified processes. The execution of this system command is facilitated by the os_exec command in Golang, which is designed to run system commands within Golang code.
Thus, it becomes evident that the second portion of the code within this graph is dedicated to the termination of processes. It does so by employing the taskkill utility, subsequent to the processes being loaded into the system.
Advancing to the subsequent node, we observe that the code is structured to handle a specific contingency: the absence of the targeted processes for termination. In such an event, it is programmed to output a message to the standard output stream, explicitly stating “Process Not Found”. This serves as a clear indication to the user that the intended processes could not be located within the system.
As we progress to the fourth graph node, it becomes apparent that, contingent upon the successful termination of the processes, the program’s control flow is directed to this particular code block. Here, we encounter the invocation of yet another function, named enc_shadowcopy_Delete. It is prudent for us to meticulously analyze this function to fully comprehend its role and functionality before we proceed further in our examination.
Indeed, the function enc_shadowcopy_Delete appears to be designed to execute the command vssadmin delete shadows /all /quiet. This command is a critical operation within the Windows environment, as it is tasked with the deletion of all shadow copies. Shadow copies are essentially snapshots or backups of computer files or volumes. The /all switch indicates that all existing shadow copies on the system are to be targeted, while the /quiet switch ensures that the operation is carried out without displaying any prompts or messages to the user, allowing for a silent execution of the command.
Moving forward to the subsequent graph node, we encounter the function config_Dirs . This function is tasked with the enumeration of all conceivable directories located on the system’s drives. It accomplishes this by systematically iterating over each of the available drives, thereby compiling a comprehensive list of directories. This process is essential for identifying potential targets for further actions within the program’s workflow.
In the fifth node of the graph, a straightforward comparison is conducted against the rax register. Depending on the outcome of this comparison, the flow of the code transitions to the sixth node. Within this sixth node, we are presented with a function named main_main_func1. It is essential to investigate this function to understand its purpose and how it integrates into the overall functionality of the program. Let’s delve into main_main_func1 and dissect its operations to gain a clearer understanding of its role.
In the initial phase, the function main_main_func1 utilizes the directories that were previously enumerated. It begins its operation by printing a message that likely reads “Walking”. This message serves as an indication that the function is about to commence the process of traversing through the directories. It’s a way to signal the start of an operation that involves examining or processing the contents of the directories identified earlier in the code.
The function main_main_func1 1 employs path/filepath.Base to extract the base name of the file from the directory path. Upon satisfying a certain condition, it proceeds to print a message, presumably “Good File”, indicating that the file meets the criteria set forth for further processing. This file is then placed into a queue, which is likely designed for locking the file, preventing other processes from modifying it while it is being processed.
After completing these tasks, the function main_main_func1 hands over control to another function, main_main_func1_1. This subsequent function is presumably responsible for continuing the operations on the queued files. To understand the complete workflow and the specific actions performed by main_main_func1_1, we would need to examine its code and logic in detail. Let’s proceed to analyze the workings of main_main_func1_1 to gain a comprehensive understanding of its functionality.
Upon examining the function main_main_func1_1, it becomes evident that it contains a call to a function named enc_encryption_EncryptFiles. This function is charged with the task of encrypting all the files that have been queued up by the preceding function. The encryption process is a critical step, ensuring that the files are secured and potentially rendered inaccessible without the corresponding decryption key or mechanism. This action aligns with the typical behavior of ransomware, which encrypts files to demand a ransom for their release. It’s important to handle such functions with caution in malware analysis and reverse engineering.
The analysis of the function main_main_func1_1 has indeed clarified that it employs the AES encryption algorithm to secure the files. Post-encryption, it modifies the file extensions to .lock, signifying their encrypted state. With this understanding, we shall now revert our focus to the calling function, main_main_func1.
This function, main_main_func1, is instrumental in the workflow as it not only identifies and queues the files for encryption but also serves as the point of transition to the encryption process itself. Let’s revisit main_main_func1 to recapitulate its operations and establish its significance in the context of the overall code execution.
Following the return of main_main_func1_1, we observe that the configuration note, which is stored within the .rdata section, is being retrieved. The subsequent action involves writing this note into a text file named “ВОССТАНОВИТЬ ФАЙЛЫ.txt”. This is accomplished through the use of the os_WriteFile function. With this step, the responsibilities of this function are fulfilled, and control is returned to the main_main function.
To summarize, we have scrutinized the functions responsible for traversing directories and encrypting files using the AES algorithm. Our analysis of the [6] graph node is now complete. This encryption pathway is applied to all files located on the target machine, across all available drives. We will now proceed to examine the final graph of interest, which is denoted as [7].
In this final segment, the MessageBox API is indeed utilized to display a critical alert to the user. The message “ВНИМАНИЕ!” translates to “ATTENTION!” in English, and it precedes a grave notification that reads, “Система вашей компании была полностью скомпрометирована. Все ваши критические данные были зашифрованы.” This can be translated to, “Your company’s system has been completely compromised. All your critical data have been encrypted.” The title of the MessageBox is “Locker”, which reinforces the severity of the situation, indicating that the user’s data has been encrypted and effectively ‘locked’.
This MessageBox serves as a stark indicator of the malicious activity that has transpired, typically associated with ransomware attacks where the user is informed that their data is inaccessible and a ransom may be demanded for decryption. It’s a critical moment in the malware’s execution flow, marking the point where the user becomes fully aware of the security breach and its implications.
About the Author:
Rudra Pratap, Security Research Manager, Gurucul
Rudra Pratap is a Security Research Manager and heads Threat Research at Gurucul with over 12 years of experience in security research and development. Rudra’s expertise spans a wide range of cybersecurity domains, including cloud & endpoint protection, threat detection & response and advanced persistent threats (APTs). He has authored multiple research papers and presented at conferences, sharing insights on topics such as industry threats and cyber espionage campaigns. With a strong background in security research, Rudra has made significant contributions to industry giants like Microsoft and FireEye.