In a quest to understand how ransomware works, I came across an article on “How not to Write a Ransomware.” This made me wonder “how do you write one then?” It led me on a trail through multiple blogs and code repositories, which only confused me more. So, I decided to write my own ransomware, to understand its operations and in the process help others who are after the same elusive secret.
Your first thoughts would be what to name your project. The name should reflect the personality of your ransomware and metaphorically describe it. With a touch of creativity, my project was rightly named after Krombopulos Michael, a Rick and Morty antagonist, dubbed KMike. The basic objectives of the project was to develop a ransomware that is:
- Demystifies the operations of a ransomware
- Has the basic functionalities of a typical modern ransomware
The next step is to decide the language you will be using to develop the ransomware. I chose Python because it is easily readable and beginner-friendly in nature. Compared to low-level languages, execution in Python is slower and it supports larger file sizes. In fact, people with malicious intentions would consider a ransomware developed in Python to be unappealing.
Stages in the lifecycle of a ransomware
This article will detail the different stages in a ransomware’s lifecycle and infection process and provide insights on how KMike operates in each stage.
A ransomware is usually embedded in documents and delivered via emails that execute as soon as it is downloaded/ opened. It may also masquerade as a legitimate software and trick you into downloading and executing it. Some ransomware might have the ability to propagate through the networks that a system is connected to.
KMike pretends to be a software that promises to help you add in-game currency in a game of your choice.
A ransomware does not start executing as soon as it is opened so as to evade detection. Instead, it performs a series of checks to determine whether it is being executed in a sandbox or a normal environment. Evasion techniques help the ransomware to encrypt files of the victims successfully and also prevent its detection. This, in turn, helps to spread the infection to other systems.
The checks are limited only by the author’s imagination. Typical checks audit the system hardware configuration for sandbox specific values, and also inspects the filename of the executable to see if it has been renamed to something like “malware” or ”test”, something analysts generally use. Reportedly, some malware also checks the CPU’s temperature, screen resolution, user interaction to evade sandboxes.
Static code analysis can be evaded if you have a codebase which was not taken from any existing malware. Even though KMike does not implement any such measures, only 7 out of 72 engines were able to detect the file as malicious.
7/72 engines detected KMike as malicious (Credits: VirusTotal)
After determining that the ransomware is not in a sandbox, the next step is to encrypt files present in the system. There are two important choices to make: what files to encrypt and how to encrypt them.
We should encrypt files that have user data in them and not the ones that are needed for the OS to function properly. We can set it in such a way that only files with specific extensions are encrypted.
We generally encrypt files with a symmetric key algorithm. This encryption scheme is generally faster and less resource consuming than asymmetric encryption. In this scheme, we generate new keys for each file that is encrypted. However, faster execution comes with a caveat that it becomes easier for analysts and researchers to break. Therefore, in the case of KMike, we encrypt all the keys that we have generated with an asymmetric key algorithm, wherein the keys are generated during execution.
Now, we have a pretty secure scheme, but the key that is used to encrypt everything is stored in the device itself for anyone to grab and decrypt the files. So, we encrypt the locally-generated private key with a public key. This public key is then embedded in the ransomware whose corresponding private key is stored in our server. With this scheme, we can encrypt all the files without making any network connections to our server. This also ensure that none of the keys are stored in plaintext form on disk.
To sum up:
- Encrypt all user files with AES-256-CBC.
- Random AES key and IV for each file.
- Encrypt AES keys with locally-generated public key RSA-2048.
- Encrypt locally-generated private key with RSA-2048 common public key
Once we have encrypted all the victim’s files, the next step is to display a ransom message and decrypt files after the payment has been made.
Ransom messages can be displayed in a variety of ways: changing the desktop background, creating a text file with the ransom message, etc. We need to make sure the server is easily accessible to the ransomware but hard for others to decipher.
This is where Domain Generation Algorithms (DGA) come in. DGAs are algorithms which are deterministic and can generate pseudorandom values meaning it will generate the same random output for a given seed. We use this to generate hundreds, if not thousands of domains and start sending requests to all the domains. Once we know the seed value, which can be something like the present date or the value of a currency at a particular time, we can randomly register a small number of the domains and make sure it is accessible for the ransomware.
We generate a unique bitcoin address for each infected machine to make it harder for adversaries to track.
Once the payment is done and verified, we can decrypt the locally generated asymmetric key from the machine in the server itself and return the decrypted key, so as to not expose the master private key at any point in time.
Thus, the lifecycle of the ransomware from infection to decryption concludes here. To see the code for KMike, please visit this repo.