# Vidar Stealer H&M Campaign **[0xtoxin-labs.gitbook.io/malware-analysis/malware-analysis/vidar-stealer-h-and-m-campaign](https://0xtoxin-labs.gitbook.io/malware-analysis/malware-analysis/vidar-stealer-h-and-m-campaign)** In this blog I'll be covering a recent phishing campaign that was targeting content creators while impersonating to a brand offering a collaboration offer to those creators. ## The Phish The email that the user receives includes a short explanation that the company wants to be his partner, they explain to him when and for how long to put the promo video and of course how much money he will receive as a payment. At the bottom of the email the user will find a link to the promotion materials and his personal password: ----- Phishing Mail The promotion materials link leads to Google Drive, there the User will need to download an archive with the name of: H&M Corporation Advertising Contract.zip The archive contains inside of it several decoy files that are associated with H&M, and a 600MB .scr file with the name: H&M Advertising contract and Payment information.pdf.scr Archive Content ## .NET Loader Opening the loader in DiE,we can see that the loader is 32bit .NET assembly protected with Smart Assembly: ----- DiE Information I've opened the loader in DnSpy to further analyze it. The first thing I see is the confirmation that the loader is protected with Smart Assembly, I can see the PoweredBy section in the static information fields: SmartAssembly Signature Looking at the entry point we can understand that working with the loader in this state won't be efficient: Loader Entry Point (with SmartAssembly) [I will be using SAE (Simple Assembly Explorer) in order to deobfuscate the code, we can use the deobfuscator](https://github.com/wickyhu/simple-assembly-explorer) feature in SAE: ----- SAE deobfuscator feature I'm using the default settings as it's fits my needs: Deobfuscator Settings Opening the deoubfuscated output file in Dnspy, we can now see a clearer code: Post Deobfuscation Entry Point ## Payload Extraction There are several interesting actions that happens in the loader: ----- 1. 1. ``` c000009 instance creation with internal field that will contain a path to the injected process. ``` c000009 Instance 2. 2. The instance then will be passed to the method c000066.m000022. this method will have several things in it, the first one being a call to the method: c000066.m00007b, passing the string: fInckSommmenn twice. 3. 3. The method c000066.m00007b will simply fetch resource content from the binary resources: Resource Fetching Method 4. 4. Then a call to the method c000066.m000019 will be invoked passing the extracted resource content, the string: **fInckSommmenn and the instance of c000009** ----- 5. 5. This method will be in charge of decrypting the payload with some Xor routine and it will return the decrypted binary. Decrypting Method 6. 6. After the decryption was done the decrypted binary will be passed alongside with the full path to the injected process to c000066.m00002a method which will do a process injection to the desired process with the decrypted binary content. Loader Main Functionality I've created a powershell script that extract the decrypted binary by invoking the necessary methods: # Load the file. $assembly = [System.Reflection.Assembly]::LoadFile("C:\Users\igal\Desktop\loader.exe") ​ #Initialize "NS005.c000009" object. $ini = [Activator]::CreateInstance($assembly.Modules[0].GetType("NS005.c000009"),@()) ​ #Retrieve the resource fetching method and invoke it. $classType2 = $assembly.GetType("NS004.c000066") $array = $classType2.GetMethod("m00007b").Invoke($null,@("fInckSommmenn", "fInckSommmenn")) ​ #Invoke the decryption method with the necessary arguments. $fixedArray = $classType2.GetMethod("m000019").Invoke($null,@($array, "fInckSommmenn", $ini)) ----- ​ #Write the output to a file. [io.file]::WriteAllBytes('C:\Users\igal\Desktop\payload.bin',$fixedArray) ## Vidar Payload In this part of the blog I will be going through some of the Vidar stealer capabilities, evasion techniques and some anti analysis tricks. Opening the payload in DiE we can see that it's a 32bit C/C++ binary: DiE Information ### Anti-Analysis Nightmare I've opened the payload in IDA and the first thing that happens is that WinMain was not recognized as a function and rather as instruction: Unrecognized WinMain I've tried to convert it to function by pressing P but this wasn't helpful, so I've scrolled a bit down and found out a chunk of data that wasn't converted as supposed: ----- Data Chunk Then I pressed C to convert that data to code and now that we have instructions instead of data I've marked all the instruction from the beginning of WinMain until the relevant mov - pop - return instructions that marks the end of a function (in my case the instructions range was 0x4156B0 - 0x415891) Now I start to work with the decompiler view, I've noticed that the decompilation process is a bit broken: ----- WinMain In Decompiler One thing that was done here to confuse the decompiler is Opaque Predicate. **_"Opaque predicate is a term used in programming to refer to decision making where there is only one_** **_possible outcome. This can be achieved through the use of complex or hard-to-understand logic, such as_** **_calculating a value that will always return True. Opaque predicates are often used as anti-disassembling_** **_techniques, as they can make it difficult for an analyst to understand the code and determine its intent. By_** **_using opaque predicates, malware authors can make their code more difficult to reverse engineer, which_** **_can help to evade detection and analysis."_** [(Unprotect Project definition)](https://unprotect.it/technique/opaque-predicate/) Opaque Predicate [We can use @_n1ghtw0lf script for it:](https://n1ght-w0lf.github.io/malware%20analysis/smokeloader/#opaque-predicates) import idc ​ ea = 0 while True: ea = min(idc.find_binary(ea, idc.SEARCH_NEXT | idc.SEARCH_DOWN, "74 ? 75 ?"), # JZ / JNZ idc.find_binary(ea, idc.SEARCH_NEXT | idc.SEARCH_DOWN, "75 ? 74 ?")) # JNZ / JZ if ea == idc.BADADDR: break idc.patch_byte(ea, 0xEB) # JMP idc.patch_byte(ea+2, 0x90) # NOP idc.patch_byte(ea+3, 0x90) # NOP After running the script the Decompiler looks a bit better: ----- Non Opaque Predicate in WinMain But there is still some code missing because we can see a JUMPOUT instruction, looking at the referenced address in the instruction, we can see that the instruction is: mov eax, 0FEB912E8h clearly that's wrong and nothing to do with the actual code (and this is caused because the conversation of all the data to code), it can be repaired by simply undefining the instruction. But after that we still can see a unclear jumpout: Jumpout instructions again same strange mov instrcution to eax: mov eax, 0FEB9C8E8h it can be fixed by the same approach as before. After clearing the code we have a "clear" function: ----- Post Cleaning WinMain The Author added a lot of junk calls to the code to make our life a bit harder but we can just ignore them and follow the function calls. ### Self Termination Triggers This Vidar payload has several triggers that can occur and lead to self termination of the payload. The first one being usage of VirtualAllocExNuma which is a way for the payload to understand whether he runs on a system with one or more physical CPU: VirtualAllocExNuma Function ----- The second check the payload does is checking the physical memory of the computer (whether it s above 769MB or not) if it's less then the defined size the payload will terminate: Check Physical Memory Function The last check will occur after the strings and api resolving functions(which will be covered in a moment), it will retrieve the computer name and compare it to HAL9TH, it will also retrieve the user name and compare it to ``` JohnDoe. if one of the retrieved values matches one of the strings the payload will terminate itself: ``` Check Computer Name Function ### Strings Decryption As most variants of Vidar, the strings are simply xor'ed. The function receives 3 parameters: 1. 1. Length 2. 2. Xor key 3. 3. Encrypted string ----- Strings Decryption Function I've used the script written by [@eln0ty and modified it a bit to fit my needs:](https://twitter.com/eln0ty) import idc ​ START = 0x401190 END = 0x40134D TEMP = 0x0 FLAG = True ''' [0] = Encrypted String. [1] = Xor Key. [2] = Length. ----- VALUES = [] ​ ea = START ​ # XOR decryption helper function. def xorDecrypt(encString, xorKey, keyLen): decoded = [] for i in range(0,len(encString)): decoded.append(encString[i] ^ xorKey[i % keyLen]) return bytes(decoded) ​ ​ ​ while ea <= END: # get argument values if idc.get_operand_type(ea, 0) == idc.o_imm: VALUES.append(idc.get_operand_value(ea, 0)) if len(VALUES) == 2: if idc.get_operand_type(ea, 0) == idc.o_reg: VALUES.append(idc.get_operand_value(ea, 1)) if idc.print_insn_mnem(ea) == "call": length = VALUES[2] data = idc.get_bytes(VALUES[0], length) key = idc.get_bytes(VALUES[1], length) VALUES = [] TEMP = ea while FLAG: ea = idc.next_head(ea, END) if (idc.print_insn_mnem(ea) == "mov") and (idc.get_operand_type(ea, 0) == idc.o_mem) and (idc.get operand type(ea, 1) == idc.o reg): ----- dec xorDecrypt(data, key, length).decode( ISO 8859 1 ) print(f'current location:{hex(ea)}, value will be: {dec}') dwordVar = idc.get_operand_value(ea, 0) idc.set_cmt(ea, dec, 1) idc.set_name(dwordVar, "STR_" + dec, SN_NOWARN) FLAG = False ea = TEMP break ​ ​ # move to next instruction FLAG = True ea = idc.next_head(ea, END) **quick note: some of the names wont be assigned properly due to IDA syntax, so I've added the plain string as** comment in the dissembler. For example: Decrypted String Comment Decoded strings output: Decoded Strings ### Dynamic API Resolving: Vidar will user LoadLibraryA and GetProcAddress to resolve the necessary API's alongside with the strings it decrypted: ----- API Resolve Function [Once again I used the script written by @eln0ty to replace the name of the variables for easier analysis:](https://twitter.com/eln0ty) import idc ​ start = 0x420874 end = 0x420901 ea = start ​ api_names = [] ​ while ea <= end: # get GetProcAddress API name if (idc.print_insn_mnem(ea) == "mov") and (idc.get_operand_type(ea, 0) == idc.o_reg) and (idc.get_operand_type(ea, 1) == idc.o_mem): addr = idc.get_operand_value(ea, 1) name = idc.get_name(addr) if name.startswith("STR_"): api_names.append(name) ​ ----- # assign GetProcAddress result to global var if (idc.print_insn_mnem(ea) == "mov") and (idc.get_operand_type(ea, 0) == idc.o_mem) and (idc.print_operand(ea, 1) == "eax"): addr = idc.get_operand_value(ea, 0) name = api_names.pop(0) idc.set_name(addr, "API_" + name[4:]) ​ # move to next instruction ea = idc.next_head(ea, end) ### C2 Communication - Init Communication In order to harvest all the data Vidar looking for, Vidar will need to utilize some DLL's which it will fetch from a C2 server, below is a short explanation of the DLL's Vidar will retrieve from the C2: **DLL Name** **Description** freebl3.dll Network Security Services (NSS) from Mozilla Foundation mozglue.dll Memory management for Mozilla applications msvcp140.dll Microsoft Visual C++ library for C++ programming nss3.dll Network security services for SSL/TLS encryption softokn3.dll Cryptographic library for key management and encryption/decryption sqlite3.dll Accessing and managing SQLite databases vcruntime140.dll Microsoft Visual C++ library for memory management and I/O In my case the Vidar C2 was hosted on 2 different sites: ----- **Telegram:** Telegram C2 URL Func ​ Telegram Channel Preview ----- **Steam:** Steam C2 URL Func ​ Steam Profile Preview And in case both of them are down, a plain C2 is presented as a backup: Plain C2 Function After retrieving the C2 Vidar will send a POST request to the URI: {C2}/{BOT_ID} In my case the bot id is: 907 which is also assigned a plain string: ----- Botnet ID Function After that first request was made the client will receive a response from the server that looks like that: 1,1,1,1,1,b36abae611984b4404a903d57724b39e,1,1,1,1,0,123;%DOCUMENTS%\;*.txt;50;true;movies:music:mp3:exe; Each operation is splitted with ; delimiter ### C2 Communication - Operations Configuration As mentioned, each operation is splitted by ; delimiter. First Section: 1,1,1,1,1,b36abae611984b4404a903d57724b39e,1,1,1,1,0,123 Most of those values are flags that says what data should be harvested: **Index** **Flag** **Description** 1 1 Local Passwords 2 1 Cookies 3 1 Crypto Wallets 4 1 Browser History 5 1 Telegram Data 6 b36abae611984b4404a903d57724b39e Exfil Token 7 1 Steam Data 8 1 Discord Data 9 1 Screenshot 10 1 Possible Grabber 11 0 File Size Limit 12 123 Profile ID **Second Section:** %DOCUMENTS%\ The grabber activity folder. **Third Section:** - txt ----- Files extensions the grabber will harvest. **Fourth Section:** 50 File size limit in KB. **Fifth Section:** true Recursive harvesting. **Sixth Section:** movies:music:mp3:exe Excluded file extensions. Additionally Vidar will create a profile for the user by harvesting the OS info, RAM, CPU, active processes etc... and will send out infromation.txt alongside with the harvested data: Version: 2.4 ​ Date: 12/2/2023 11:15:46 MachineID: 4cfb5922-b036-4c14-9ed1-03c0dad19fbd GUID: {d6dc608d-2a27-11ed-a0e3-806e6f6e6963} HWID: 12ac9eab3d083674480464-4cfb5922-b036-4c14-9ed1-a0e3-806e6f6e6963 ​ Path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe Work Dir: In memory ​ Windows: Windows 10 Pro [x64] Install date: 8/12/2021 0:18:31 AV: Unknown Computer Name: IYMUGYHL User Name: Admin Display Resolution: 1280x720 Display Language: en-US Keyboard Languages: English (United States) Local Time: 12/2/2023 11:15:47 ----- TimeZone: UTC 0 ​ [Hardware] Processor: Intel Core Processor (Broadwell) Cores: 2 Threads: 2 RAM: 4095 MB VideoCard: Microsoft Basic Display Adapter ​ [Processes] - System [4] - Registry [92] - smss.exe [348] - csrss.exe [436] - wininit.exe [512] - csrss.exe [520] - winlogon.exe [604] - services.exe [644] - lsass.exe [656] - fontdrvhost.exe [764] - fontdrvhost.exe [772] - svchost.exe [780] - svchost.exe [884] - svchost.exe [932] - dwm.exe [1016] - svchost.exe [60] - svchost.exe [720] - svchost.exe [640] - svchost.exe [1044] - svchost.exe [1052] ----- svchost.exe [1140] - svchost.exe [1192] - svchost.exe [1208] - svchost.exe [1232] - svchost.exe [1316] - svchost.exe [1384] - svchost.exe [1432] - svchost.exe [1452] - svchost.exe [1504] - svchost.exe [1572] - svchost.exe [1604] - svchost.exe [1616] - svchost.exe [1712] - svchost.exe [1740] - svchost.exe [1840] - svchost.exe [1876] - svchost.exe [1900] - svchost.exe [1952] - svchost.exe [1968] - spoolsv.exe [1296] - svchost.exe [1944] - svchost.exe [2064] - svchost.exe [2100] - sihost.exe [2288] - svchost.exe [2296] - taskhostw.exe [2436] - svchost.exe [2488] - svchost.exe [2496] - OfficeClickToRun.exe [2552] - svchost.exe [2560] ----- svchost.exe [2616] - svchost.exe [2656] - svchost.exe [2668] - svchost.exe [2676] - svchost.exe [2976] - explorer.exe [3048] - svchost.exe [2832] - dllhost.exe [3248] - StartMenuExperienceHost.exe [3356] - RuntimeBroker.exe [3416] - dllhost.exe [3456] - SearchApp.exe [3568] - RuntimeBroker.exe [3688] - RuntimeBroker.exe [4652] - svchost.exe [4340] - svchost.exe [1892] - svchost.exe [3392] - svchost.exe [4424] - svchost.exe [4680] - sppsvc.exe [1096] - svchost.exe [1260] - svchost.exe [2544] - WmiPrvSE.exe [1348] - SppExtComObj.Exe [2532] - svchost.exe [2596] - svchost.exe [3020] - upfc.exe [4400] - svchost.exe [1632] - H&M Advertising contract and Payment information.pdf.scr [4396] - vbc.exe [1684] ----- ​ [Software] Google Chrome [89.0.4389.114] Microsoft Edge [92.0.902.67] Microsoft Edge Update [1.3.167.21] Microsoft Visual C++ 2012 Redistributable (x86) - 11.0.61030 [11.0.61030.0] Java Auto Updater [2.8.66.17] Microsoft Visual C++ 2015-2022 Redistributable (x86) - 14.30.30704 [14.30.30704.0] Microsoft Visual C++ 2015-2022 Redistributable (x64) - 14.30.30704 [14.30.30704.0] Microsoft Visual C++ 2013 Redistributable (x86) - 12.0.40660 [12.0.40660.0] Microsoft Visual C++ 2013 x86 Additional Runtime - 12.0.40660 [12.0.40660] Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.6161 [9.0.30729.6161] Adobe Acrobat Reader DC [19.010.20069] Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030 [11.0.61030] Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030 [11.0.61030] Microsoft Visual C++ 2022 X86 Additional Runtime - 14.30.30704 [14.30.30704] Microsoft Visual C++ 2012 Redistributable (x64) - 11.0.61030 [11.0.61030.0] Microsoft Visual C++ 2013 x86 Minimum Runtime - 12.0.40660 [12.0.40660] Microsoft Visual C++ 2013 Redistributable (x64) - 12.0.40660 [12.0.40660.0] Microsoft Visual C++ 2010 x86 Redistributable - 10.0.40219 [10.0.40219] Microsoft Visual C++ 2022 X86 Minimum Runtime - 14.30.30704 [14.30.30704] ### C2 Communication - Data Exfiltration After harvesting all the data Vidar will compress all harvested data to as a zip encode it to base64 and send it out alongside with some more data in the next format: ------{random_generated_delimiter} Content-Disposition: form-data; name="profile" ​ {BOT_ID} ------{random_generated_delimiter} Content-Disposition: form-data; name="profile_id" ----- {PERSONAL_ID} ------{random_generated_delimiter} Content-Disposition: form-data; name="hwid" ​ {COMPUTER_HWID} ------{random_generated_delimiter} Content-Disposition: form-data; name="token" ​ {EXFIL_TOKEN} ------{random_generated_delimiter} Content-Disposition: form-data; name="file" {BASE64_ENCODED_ARCHIVE} ----- Exfiltration Function ### Post Exfiltration Self Termination After Vidar exfiltrated the data it will create a self termination task using cmd command and by this will end the execution of itself: "C:\Windows\System32\cmd.exe" /c timeout /t 6 & del /f /q Vidar.exe & exit ----- Self Termination Function ## Summary Vidar is a well known stealer that was active for the past years and keeps on constantly updated by its developers. In this blog we've covered most Vidar functions and how it was delivered to it's victims. Quick note that it's my first "In Depth" writeup for a malware so any feedback would be appreciated, you can always [PM me on twitter (0xToxin)](https://twitter.com/0xToxin) ## Yara Rule [The rule is updated up to version 2.4 which was recently revamped from version 5X.X (more info can be found here)](https://twitter.com/AnFam17/status/1626659725709398016) rule win_Vidar { meta: author = "0xToxin" description = "Vidar stealer strings and functions" Date = "20-02-2023" strings: ----- $dll1 vcruntime140.dll ascii wide $dll2 = "softokn3.dll" ascii wide $dll3 = "nss3.dll" ascii wide $dll4 = "msvcp140.dll" ascii wide $dll5 = "mozglue.dll" ascii wide $dll6 = "freebl3.dll" ascii wide $dll7 = "sqlite3.dll" ascii wide $c2Fetch1 = "t.me" ascii wide $c2Fetch2 = "steamcommunity.com" ascii wide $stringDec = { 68 ?? ?? ?? 00 68 ?? ?? ?? 00 B9 ?? ?? 00 00 E8 ?? ?? ?? ?? 68 ?? ?? ?? 00 68 ?? ?? ?? 00 B9 ?? ?? 00 00 A3 ?? ?? ?? ?? } condition: uint16(0) == 0x5a4d and 3 of ($dll*) and 1 of ($c2Fetch*) and #stringDec >= 15 } [You can see also the Yara Hunt result on UnpackMe.](https://www.unpac.me/yara/results/17be75d5-e9cc-43e3-991a-ff05fefed65f) ## IOC's **Samples:** H&M Corporation Advertising Contract.zip [4d9697358936b516ecd2dd96687649fc1a8b1e8fd4529961dfa49513c85b42c5​](https://bazaar.abuse.ch/sample/4d9697358936b516ecd2dd96687649fc1a8b1e8fd4529961dfa49513c85b42c5/) H&M Advertising contract and Payment information.pdf.scr [203b08962eba219761690043281f81fc2d6e1fa26702bfa4ad30d9849b267309​](https://bazaar.abuse.ch/sample/203b08962eba219761690043281f81fc2d6e1fa26702bfa4ad30d9849b267309) vidar.bin - [dd15f493fc13d00bb1abc0ac20bb0f7dc44632e71b4fcde1c2889fc34dff6c14​](https://bazaar.abuse.ch/sample/dd15f493fc13d00bb1abc0ac20bb0f7dc44632e71b4fcde1c2889fc34dff6c14/) ----- **Fetching URL s:** https://steamcommunity.com/profiles/76561199476091435 https://t.me/gurutist **C2's:** 195.201.44.125 23.88.36.149:80 95.216.164.28:80 ## References Malware Analysis - Previous AsyncRAT OneNote Dropper Last modified 8d ago -----