Introduction
While reviewing samples submitted to Any.Run, I came across a binary that appeared to inject into a target process before performing some suspicious HTTP requests. After further analysis, this binary was found to be consistent with the VIDAR infostealer. This article aims to explain exactly how the infostealer works, the loader chain, what it attempts to steal, and how it exfiltrates data stolen from the host in a short time without leaving a trace.
Since this sample already had a dynamic, sandbox run within Any.Run, I decided to download a copy and attempt to reverse engineer it with the aim of understanding more about how it worked, what it was designed to do, and to attempt to identify the malware family.
The sample being analyzed in this post has the following file hashes:
- MD5:
9BB9FD7110158BEA15B3EB3881C52606
- SHA1:
F545BF2A5E310ED8E9A8F553DA11B5C03D859A79
- SHA256:
F2FEEFF2C03FE54E6F8415390CFA68671576D4CA598C127B5C73B60864E7372B
The entire infection chain and malware operation can be summarized at a high level by this diagram:
About VIDAR
Although this is not unique to the VIDAR malware family, this infostealer performs a smash-and-grab approach to harvesting data by harvesting as much data from the host and exfiltrating as quickly as possible. Public reporting shows that delivery mechanisms for the VIDAR infostealer include fake software installers, Windows 11 installers and even malicious Microsoft help files delivered via phishing.
Since the service provided to the customers of VIDAR exists only to facilitate payload configuration, generation and C2, it is left to the customer to get the malware on to systems; whether themselves or via a third-party malware distribution service. An example of such a malware distribution service is a group Microsoft tracks as DEV-0569, who leverage techniques such as malvertising, phishing etc. to distribute BATLOADER; a separate malware designed to deliver additional payloads, including VIDAR, ROYAL ransomware and COBALT STRIKE.
Loader Analysis
Based on the sandbox run, the binary appears to spawn a child process instance of AppLaunch.exe
, which is a legitimate binary and part of the .NET framework. The suspicious activity performed by the malware then appears to originate from this legitimate process, which is a good indicator that some form of process injection is occurring.
Initial Assessment
The binary being analyzed has the following characteristics:
- MD5:
9BB9FD7110158BEA15B3EB3881C52606
- SHA1:
F545BF2A5E310ED8E9A8F553DA11B5C03D859A79
- SHA256:
F2FEEFF2C03FE54E6F8415390CFA68671576D4CA598C127B5C73B60864E7372B
- Compiled Timestamp:
Fri Dec 23 14:09:41 2022 UTC
- Linker:
Linker GNU linker ld (GNU Binutils)
The executable itself has very few imports, which indicates that there is some form of obfuscation going on to conceal the malware’s true purpose and functionality.
First Stage Loader
This first loader works by XOR decrypting at runtime both the second stage malware, and an additional loader shellcode, which loads the final stage payload. It then stores a pointer to the decrypted loader shellcode in the register edx
, that was previously made executable using VirtualProtect
. Next, it pushes the parameters to be passed to the loader function (consisting of a pointer to the decrypted second stage payload and a filepath to the injection target) on to the stack before executing it with a call edx
instruction.
Second Stage Loader
The second stage loader performs the process injection to load the final stage payload into memory. In this case, it used a process hollowing but not really approach to injecting the VIDAR binary into an AppLaunch.exe
process.
The process injection method uses the following sequence of API calls:
NtCreateUserProcess
VirtualAlloc
VirtualAllocEx
WriteProcessMemory
ResumeThread
Firstly, NtCreateUserProcess
is called with the following arguments to spawn the process injection target in a suspended state.
NtCreateUserProcess
is an undocumented function, however more information about this function can be found here, and has been well documented by security researchers. For the purposes of this loader, two important arguments are passed:
CreateThreadFlags
, which is set to0x01
, and corresponds to starting the process in a suspended state.ProcessParameters
, which is aRTL_USER_PROCESS_PARAMETERS
structure, and within it the image pathC:\Windows\Microsoft.NET\Framework\v4.0.30319\AppLaunch.exe
is provided.
Next, the loader calls both VirtualAlloc
and VirtualAllocEx
to allocate a region in the memory space of the target process for the final payload to be injected.
VirtualAlloc Arguments
VirtualAllocEx Arguments
As per the documentation for these functions, both calls pass in the same arguments. The only difference being that VirtualAlloc
is not provided a lpAddress
pointer, which results in the function returning a pointer to the allocated memory region, whereas the hProcess
argument provided to VirtualAllocEx
is 0x009C
. Aside from those differences, the arguments provided to both functions are the same:
dwSize
:0x67000
, which corresponds to421888
bytes.flAllocationType
:0x03000
, which corresponds to both committing and reserving the address range in one step.flProtect
:0x40
, which corresponds to read, write and execute (RWX
) permissions.
These API calls serve to allocate a region in memory for the target AppLaunch.exe
process that is readable, writable and executable, which allows the loader to finally write the bytes for the finaly payload into this memory region within the target process using WriteProcessMemory
. Finally, the injected process is then resumed from its suspended state using ResumeThread
, which allows it to execute the injected payload.
Dumping the Injected Payload
Dumping the injected payload was straight forward using x32dbg. All that was needed was a breakpoint to be set to pause execution when the malware reaches the WriteProcessMemory
call.
bp WriteProcessMemory
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer,
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
WriteProcessMemoryArguments
The arguments passed to WriteProcessMemory
correspond to the following:
hProcess
:0x009C
, which is the same process handle passed into the previousVirtualAllocEx
function call.lpBaseAddress
:0x00400000
, which corresponds to the image base address in which the process memory is written.lpBuffer
:0x02EA0000
nSize
:0x67000
, which corresponds to the same421888
bytes.
The argument of interest is lpBuffer
, which is the pointer to the memory region containing the bytes to be written to the memory space of the new process.
As shown, this memory region starts with a MZ
header and DOS string, which means this memory region must contain the binary that is being injected into AppLaunch.exe
. The final stage executable payload can then be retrieved by dumping the memory region to disk.
Realigning the Dumped PE
When viewing the dumped executable in PEBear, or CFF Explorer, both programs were unable to determine the presence of any imports used by the binary. This is actually because the PE recovered from memory was in a mapped format. This caused a misalignment of section offsets within the raw executable on disk and therefore resulted in a misaligned import address table (IAT). This video explains the concept far better than I can.
Fortunately, we can edit the section table to unmap the sections within the executable and realign the IAT. By setting the raw address offsets to be equivalent to the virtual address offsets, and modifying the section sizes accordingly.
Now the executable has been correctly aligned, the module and function imports are now readable.
Since we now have an unmapped exectable, it is possible to reverse engineer and analyze the resulting payload. In this case, the reconstructed executable is written in C++.
VIDAR Infostealer Analysis
Once the dumped payload has been realigned and otherwise fixed to become a valid portable executable, the second stage executable has the following characteristics:
- MD5:
47a6959ac869f65dd31e65b1c80fa8b2
- SHA1:
f9d9ecf59523c202bca9ac4364b2a2042f116f32
- SHA256:
1521e9e7b06676a62e30e046851727fe4506bdf400bcf705a426f0f98fba5701
- Compiled Timestamp:
Mon Dec 19 12:33:40 2022 UTC
- Compiler:
Microsoft Visual C/C++
- Linker:
Microsoft Linker 10.0 - (Visual Studio 2010)
Encrypted Strings & Module Imports
The VIDAR malware stores some of its strings, and module imports in a base64-encoded, RC4 encrypted format. By combining the different functions in the binary, along with the decrypted and decoded strings, we arrive at a full list of browser extensions targeted by the malware. These are mainly cryptocurrency wallets, but also include two factor authentication (2FA) extensions, and other password managers.
Before doing anything else, VIDAR first calls a built-in routine to base64-decode and RC4 decrypt each string before writing it into a memory region. A pointer to each string and module import can then be referenced by the malware to use them.
The malware leverages the BCryptDecrypt
API call to decode base64, and a custom-coded RC4 decryption function.
Cryptocurrency wallets and 2FA browser extensions and applications targeted by VIDAR
TronLink
MetaMask
BinanceChainWallet
Yoroi
NiftyWallet
MathWallet
Coinbase
Guarda
EQUALWallet
JaxxLiberty
BitAppWallet
iWallet
Wombat
MewCx
GuildWallet
RoninWallet
NeoLine
CloverWallet
LiqualityWallet
Terra_Station
Keplr
Sollet
AuroWallet
PolymeshWallet
ICONex
Harmony
Coin98
EVER Wallet
KardiaChain
Trezor Password Manager
Rabby
Phantom
BraveWallet
Oxygen (Atomic)
PaliWallet
BoltX
XdefiWallet
NamiWallet
MaiarDeFiWallet
WavesKeeper
Solflare
CyanoWallet
KHC
TezBox
Temple
Goby
Authenticator
Authy
EOS Authenticator
GAuth Authenticator
Tronium
Trust Wallet
Exodus Web3 Wallet
Braavos
Enkrypt
OKX Web3 Wallet
Sender
Hashpack
Eternl
GeroWallet
Pontem Wallet
Petra Wallet
Martian Wallet
Finnie
Leap Terra
Microsoft AutoFill
Bitwarden
KeePass Tusk
KeePassXC-Browser
Bitwarden
Ethereum\Ethereum\
Electrum\Electrum\wallets\
ElectrumLTC\Electrum-LTC\wallets\
Exodus\exodus\conf.json,window-state.json
\Exodus\exoduswallet\passphrase.json,seed.seco,info.seco
ElectronCash\ElectronCash\wallets\default_wallet
MultiDoge\MultiDoge\multidogewallet
Jaxx_Desktop_Old\jaxx\Local Storage\file__0localstorage
Binance\Binance\app-store.json
Coinomi\Coinomi\wallets\
*wallets
*config
wallet_path
SOFTWARE\monero-project\monero-core,\Monero\
Also encrypted in the binary are the SQL queries used to harvest data stored in web browsers:
SQL queries used by VIDAR to harvest browser data
SELECT origin_url, username_value, password_value FROM logins
SELECT name, value FROM autofill
SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards
SELECT target_path, tab_url from downloads
SELECT url FROM urls
SELECT HOST_KEY, is_httponly, path, is_secure, (expires_utc/1000000)-11644480800, name, encrypted_value from cookies
In addition, one of the encrypted strings, C:\ProgramData\
is the staging directory used by the malware when it downloads additional libraries and stages data for exfiltration. This staging directory appears to be consistent across all VIDAR samples.
Retrieving C2 Servers
The malware stores its C2 servers in an unencrypted format within the binary:
hxxps://t[.]me/traduttoretg
hxxps://steamcommunity[.]com/profiles/76561199445991535
hxxp://5.75.253[.]16:80
Interestingly, the VIDAR malware leverages legitimate services to host C2 configuration data. For example, the Telegram and Steam profile links contain IP addresses, whereas the 5.75.253[.]16
appears to be consistent with threat actor infrastructure. This way, the malware operators can contiunously cycle C2 servers and have the malware beacon back to each new IP address so long as the social media profiles hosting the C2 IP address remain active.
This particular VIDAR sample uses the string grundic
to identify where on the webpage the C2 address is, and grabs the string up until the ending |
character.
Initial C2 Callback
Once the malware has retrieved a C2 IP address from one of the social media profiles hardcoded within the binary, it then submits a HTTP GET
request containing the profile ID of the affiliate. Since the VIDAR malware is sold as an infostealer-as-a-service where the cybercriminal gains access to a control panel to configure and generate the malware, this ID is used to retrieve the configuration data set by the VIDAR customer. In this sample, the profile ID is 1375
.
VIDAR configuration retrieved
1,1,1,1,1,36bfd46626a0b531909b016919dd1fbd,1,1,1,1,0,Default;%DOCUMENTS%\;*.txt;50;true;movies:music:mp3;desktop;%DESKTOP%\;*.txt:*.doc:*.docx:*.xlsx:*.xlsm:*.xls:*.pptx;950;true;movies:music:mp3:exe;
Downloading Additional Libraries
To be able to perform its full credential harvesting tasks, the VIDAR malware must download additional DLL libraries to extend its capability. For example, to interact with web browsers or use SQLite3.
In previous samples, the download of these additional libraries was achieved by sending a HTTP GET
request to the C2 server for a ZIP file named with random alphanumeric characters. In an apparent change of technique, or configuration, this sample retrieves the additional libraries by downloading a resource from the C2 server named update.zip
.
Nevertheless, once the ZIP file containing the DLLs is downloaded, it is extracted and the DLL binaries are saved to the C:\ProgramData
staging directory. The resource update.zip
contained the following DLLs:
MD5 (freebl3.dll) = ef2834ac4ee7d6724f255beaf527e635
MD5 (libcurl.dll) = 37f98d28e694399e068bd9071dc16133
MD5 (mozglue.dll) = 8f73c08a9660691143661bf7332c3c27
MD5 (msvcp140.dll) = 109f0f02fd37c84bfc7508d4227d7ed5
MD5 (nss3.dll) = bfac4e3c5908856ba17d41edcd455a51
MD5 (softokn3.dll) = a2ee53de9167bf0d6c019303b7ca84e5
MD5 (sqlite3.dll) = e477a96c8f2b18d6b5c27bde49c990bf
MD5 (vcruntime140.dll) = 7587bf9cb4147022cd5681b015183046
Data Exfiltration
The harvested data is then sent back to the C2 server using a HTTP POST
request.
HTTP POST
body
------1531306219445135
Content-Disposition: form-data; name="profile"
1375
------1531306219445135
Content-Disposition: form-data; name="profile_id"
1700
------1531306219445135
Content-Disposition: form-data; name="hwid"
d8d914bc22c31291311131-90059c37-1320-41a4-b58d-816d-806e6f6e6963
------1531306219445135
Content-Disposition: form-data; name="token"
36bfd46626a0b531909b016919dd1fbd
------1531306219445135
Content-Disposition: form-data; name="file"
UEsDBBQAAgAIAMSYl1XqxfupygAAAJMB...[truncated base64-encoded ZIP file]
------1531306219445135--
Interestingly, the token 36bfd46626a0b531909b016919dd1fbd
matches the string contained within the initial config downloaded from the C2 server.
The data harvested from the host is stored within the ZIP file in the POST
request body.
Directory structure of the ZIP file
/History/Mozilla Firefox_qldyz51w.default.txt
/Cookies/Google Chrome_Default.txt
/History/Google Chrome_Default.txt
/passwords.txt
/information.txt
/Files/Default.zip
/Files/desktop.zip
/screenshot.jpg
It is important to note that this is not an exhaustive list of what the ZIP file exfiltrated from every system will look like. It will depend on both the stealer configuration set by the threat actor during the payload generation stage, and the software present on the compromised system. For example, the sample analyzed in this writeup included routines for stealing Discord tokens, data from Telegram and even FTP and SCP clients.
Cleanup Operations
Once the data has been succesfully harvested and exfiltrated, the malware then deletes itself and any created files from the host with the following command:
"C:\Windows\System32\cmd.exe" /c timeout /t 6 & del /f /q "C:\Windows\Microsoft.NET\Framework\v4.0.30319\AppLaunch.exe" & exit