Learning Cryptography Through Analyzing Ransomware: Jigsaw and Turkstatik

In the malware family, ransomware is like a flashy cousin who gets all the attention. The immediate impact and psychological terror of ransomware bring it an incredible amount of recognition and visibility. Today we will take a look at 2 ransomware: Jigsaw and TurkStatik

1. Jigsaw


From looking as the Import, Jigsaw appears to be a .Net application which is great news.

Instead of IDA, you can use any commercial  .Net decompilers to get the accurate C# codes. I highly recommend dnSpy . It's FREE while also having debugging functionality.

Opened in dnSpy, this sample is unpacked and non-obfuscated. First. we start our analysis by looking at the Resources section


There are 3 pieces of information stored here: an array of "supported" extensions, the JigsawImage, and the crypto wallet address. Next, we move on to the code section inside each module. Conveniently, Main->Config stores all the important information about our ransomware. Here, we can find the EncryptionPassword is hardcoded to a base64 string, and by right-click->Analyze, we are led to the encryption routine. Other interesting stuffs also can be found here such as the RansomUsd set to 100, the WelcomMessage aka ransom note, the ransom extension of ".NDGHacks", and the TimerActivateCheckerInterval of 6000.

Countdown mechanics

Unlike other ransomware, Jigsaw puts you on the clock by threatening to delete your files. Just as promised in the WelcomMessage, it uses "exponential growth" to decide the faith of your data. Specifically, the timerCountDown_Tick function in FormGame module implements the below formula:

num_files_to_delete = 1.1(i = i +1 every 3600 seconds)

If the user is verified to pay off the amount equals to RansomUsd, the countdown will indeed be stopped.

Outdated Payment Verification Method

Next, we see how Jigsaw verifies the payment from the users. In Main.Tools->Blockr moudle, 2 functions 
GetPrice  and GetBalance use block.io API to verify whether the amount of bitcoin equivalent to  RansomUsd is transferred. Tho, the API hook is a little bit outdated:


There is a possibility that the verification may fail even when the threat actor intends to implement a legit bailout system.

Locker module

The module at Main.Tools-> Locker is in charge of file encryption and decryption. Interestingly,  the Jigsaw's Locker module uses standard AES as their cipher, similar to the Locker ransomware. The key and initialization vector are hardcoded, which means recovering the original files is a piece of cake. One thing to notice is that the data will be decrypted with a block size of 65536 bytes. I wrote a python script to recover the original data using the following CLI:

py recover.py <filename> -k <base64 key string> -iv <example: 0,1,0,3,5,3,0,1,0,0,2,0,6,7,6,0>

2. TurkStatik


Similar to the Jigsaw, TurkStatik is another .Net ransomware. This specific sample is also unpacked and non-obfuscated. Unlike Jigsaw tho, TurkStatik is pretty boring to analyze...until you try to replicate its encryption and decryption routine in Python. TurkStatik uses a strange combination of C# crypto library functions and parameters that are just not available in Python libraries. In order to demonstrate my point; first we need to understand the overview of TurkStatik encryption mechanics.

Overview

On the first run, 3 pieces of information: uid, salt, and initialization vector (iv) are first encrypted then written to $Temp/windows.dll. Uid is a random number generated from RNGCryptoServiceProvider, while salt and iv are hardcoded values. If the windows.dll file already exists, it will be read and decrypted to generate these 3 variables. Among them, only salt and iv are used to encrypt users' files, before the ".ciphered" extension is added.

windows.dll

This encryption of data to generate the windows.dll file is a standard AES. The trick here is how the key is derived from a hardcoded passPhrase

Rijndael vs AES

A lot of people use Rijndael and AES interchangeably, but in fact, AES is just a subset of Rijndael. AES requires a fixed block size of 128 bit, and supports key size of 128, 192, or 256 bits. On the other hand, Rijndael supports any block size and key size as long as they are a multiple of 32 and in the range of 128-256 bits. When the block size is 128 bits, AES and Rijndael produce the same result.

On the above algorithm, the RijandaeManged object is set up with a default setting which is equivalent to a standard AES cipher. The key is derived to 256 bits using PasswordDeriveBytes(passPhrase, salt).GetBytes(32) where the passPhrase is a hardcoded string and the salt is null. An equivalence of this function in Python would be:

Crypto.Protocol.KDF.PBKDF1(passwordsaltdkLencount=1000hashAlgo=None)

where salt (byte string) – An 8 byte string. If a None, or an empty string is provided as salt, we won't be able to get the same result as PasswordDeriveBytes(). My attempt with PBKDF2 also failed, which means we have to implement our own key derive logic.

Reversing PasswordDeriveBytes() and GetBytes()

passPhrase: "8b%CA2o{a}4KGg&75Sz!L$3jcX/96iH*fT_46?wM=J2nZs-8m+"
Derived key59 200 230 192 190 120 240 88 119 206 222 25 183 240 23 211 98 159 46 60 161 31 64 232 255 167 106 70 50 171 24 195

The first 20 bytes of the key is generated from performing SHA1 hash on (pass +salt) 100 times. Since the salt is None, SHA1 hashing the pass 100 times will get us the result. The last 12 bytes are produced by the same method but with a small tweak. In the last iteration, a prefix is added to the beginning of hash input. This number is equal to the ASCII presentation of the number of rounds needed to lengthen the password. After successfully derived the key in python, the next step is performing the standard AES decryption on the windows.dll data and parse them into 3 attributes uidsalt, and iv. The full implementation can be found here

Reversing  Encryption and Decryption Routine

After retrieving the key, and iv we can start working on recovering the hacked files. TurkStatik uses Rijndael again but this time with 256 bits/ 32 bytes block size and key size:

    rijndaelManaged.Padding = PaddingMode.Zeros;
    rijndaelManaged.Mode = CipherMode.CBC;
    rijndaelManaged.KeySize = 256;
    rijndaelManaged.BlockSize = 256;
    rijndaelManaged.Key = key;
    rijndaelManaged.IV = iv;

Hilariously, I could not find a single decent Python implementation of Rijndael. The best one that I came across is https://github.com/moeenz/rijndael , but it's outdated and has many bugs. After spending a few hours fixing this library to support bytearray (only accept string), I still couldn't get the result that I wanted. So I decided to write the decryption routine in C# and come back to this project another time. I used the first 32 bytes of a ransomed Chrysanthemum.jpeg for testing and the result is positive. You can find the code and test sample here

Comments

Popular posts from this blog

3 Levels of Unpacking For Newbies. Part #1: Conceptually Unpack UPX

3 Levels of Unpacking For Newbies. Part #2: Stubbornly Unpack VB6 RunPE

A Police Department Scam on Zalo App that leads to an Android SpySMS variant