Fickle Stealer Distributed via Multiple Attack Chain

Affected Platforms: Microsoft Windows
Impacted Users: Microsoft Windows
Impact: The stolen information can be used for future attack
Severity Level: High

The past few years have seen a significant increase in the number of Rust developers. Rust is a programming language focused on performance and reliability. However, for an attacker, its complicated assembly code is a significant merit.

In May 2024, FortiGuard Labs observed a Rust-based stealer. In addition to its intricate code, the stealer is distributed using a variety of strategies and has a flexible way of choosing its target. Because of this ambiguity, we decided to call it Fickle Stealer.

This article summarizes the details of this campaign, roughly dividing the attack chain into three stages: Delivery, Preparatory Work, and Packer and Stealer Payload.

Attack flow diagram

Figure 1: Attack flow


We are aware of four methods being used to deliver Fickle Stealer: VBA dropper, VBA downloader, link downloader, and executable downloader. For the most part, they download a PowerShell script for preparatory work. The file name is u.ps1 or bypass.ps1—they indicate the same file. In some attack chains, one more file is added between the downloader and u.ps1

  • VBA dropper
    This attack chain starts with a Word document. Its VBA macro loads an XML file stored in the caption of a UserForm object and executes a script encoded with Windows Script Encoder in the XML file.
The VBA code loads an XML file and executes the encoded sript

Figure 2: The VBA code executes the encoded script

The script in the XML file drops Fickle Stealer to the Temp folder and executes it.

The decoded script

Figure 3: The decoded script.

  • VBA downloader
    There are three kinds of VBA downloaders. All of them are Word documents. The first one downloads u.ps1 directly.
VBA code in the first downloader downloads u.ps1 directly

Figure 4: The VBA code in the first downloader

The second VBA downloader uses forfiles.exe to subvert detections that limit cmd usage.

VBA code in the second downloader runs runOnce.bat which executes u.ps1 with PowerShell

Figure 5: runOnce.bat only executes u.ps1 with PowerShell

The third downloader uses a trick to indirectly deliver the VBA downloader. In the document, a web browser control that accesses an MSHTML file on the server is embedded in a frame. When the victim enables active content and macro, it reads the MSHTML file and extracts the command from the file. Usually, the WebBrowser.Navigate method is necessary to load a specified URL. However, Word stores the last loaded URL in the document file, and that URL is used if a new one is not provided. In other words, once the URL is loaded, it can be loaded in the next execution even though there is no related macro. Another variant uses this technique to hide the URL (8d3ccfafc39830ee2325170e60a44eca4a24c9c4dd682a84fa60c961a0712316).

Screenshot of the code in a Word document macro that downloads the VBA downloader and removes itself after the URL is loaded

Figure 6: The orange-underlined code can be removed after the URL is loaded

  • Link downloader
    The link downloader directly downloads bypass.ps1.
link downloader refers to the command executing the PowerShell script

Figure 7: The link downloader refers to the command executing the PowerShell script

  • Executable downloader
    The executable downloader is a DotNet executable mimicking a PDF viewer. 
The executable downloader is a file called PDFium.exe with the description PDF viewer.

Figure 8: The executable downloader

Preparatory Work

This section introduces the script files used in this attack.

  • Bypass.ps1/u.ps1
    The primary purpose of this script is to bypass User Account Control (UAC) and execute Fickle Stealer. Additionally, it creates a new task that executes engine.ps1 after 15 minutes. To bypass UAC, u.ps1 drops a copy of WmiMgmt.msc and a fake WmiMgmt.msc to the following paths:
Normal: C:\Windows \System32Fake: C:\Windows \System32\en-US

An MSC file, hosted in Microsoft Management Console (MMC), manages the hardware, software, and network components and requires admin rights. Snap-ins provide the interface to the management task and access to the required program and data. The fake WmiMgmt.msc abuses a Shockwave Flash Object from ActiveX control, which opens a web browser by default.

Settings in the fake WmiMgmt.msc

Figure 9: Settings in the fake WmiMgmt.msc

The URL for the web browser is set to localhost, and u.ps1 creates HttpListener, which shows a web page when WmiMgmt.msc is executed. The web page contains a script that configures exclusions for Fickle Stealer and then downloads it to be executed.

The web page code

Figure 10: The web page provided by u.ps1

The file path uses a technique called the Mock Trusted Directories Method. When converting a string during an evaluation request process, the trailing space after “Windows” is removed. As a result, the WmiMgmt.msc will be treated as executed from a trusted path.

Furthermore, MMC searches the MSC file for local languages. If not found, it tries to find one for en-US, so when Fickle Stealer executes WmiMgmt.msc’s copy, the fake WmiMgmt.msc is executed instead, with elevated authentication and no UAC prompt pops up.

An illustration of the priority level for languages using the example of the Chinese (Taiwan) language code, or zh-TW. The MMC first tries to find the file in the zh-TW folder, then the zh-Hant folder, then the zh folder, then en-US and en, then the parent System32 folder.

Figure 11: The MSC for local language has a higher priority

  • engine.ps1 & inject.ps1
    engine.ps1 enumerates exe files in C:\Users\, D:\, E:\, F:\. When a file is found, it runs inject.ps1 to inject shell code, which simply executes u.ps1 from the internet. The paths of injected files are base64 encoded and written to C:\Users\Public\prepares.dat. Before injection, engine.ps1 checks the list to prevent double-injection.
Injected shell code

Figure 12: Injected shell code.

  • tgmes.ps1
    u.ps1, engine.ps1, and inject.ps1 send messages frequently to the attacker’s Telegram bot to show their current condition. To send a message, they download tgmes.ps1 to the Temp folder with a random file name and execute it with the message as an argument. tgmes.ps1 is then deleted immediately. This occurs every time a message is sent.
Code for sending a message

Figure 13: The code for sending a message

Besides the message, tgmes.ps1 sends victim information, including country, city, IP address, OS version, computer name, and user name to the Telegram bot.

Data sent to Telegram bot

Figure 14: The data sent to the Telegram bot


Fickle Stealer is protected by a packer disguised as a legal executable. It seems that the attacker made the packer by replacing some code of a legal executable with the packer’s code and changing a function called in the initialize routine into the packer’s function. This can frustrate the static analysis. Mimicking various applications makes it difficult to detect the malware using certain detection rules.

For example, there is a variant (a641d10798be5224c8c32dfaab0dd353cd7bb06a2d57d9630e13fb1975d03a53) whose __cinit function in the initialize routine is modified into the packer’s function. 

Comparison between the code in the clean and malicious programs

Figure 15: Comparison between the legal program and the packer for Fickle Stealer

In this case, the malicious code is executed before the WinMain function, which is usually the user-provided entry point for a C/C++ GUI application. As a result, people following typical analysis rules may overlook the malicious code. The packer only allocates memory to write the decrypted payload data and then executes it in memory.

Stealer Payload

Fickle Stealer execution flow diagram. It starts off by creating a mutex and performing anti-analysis checks, which creates a fake message. While the fake message is being analyzed, the stealer exits the process. It sends stolen data to the attacker, takes screenshots, and then deletes itself and exits the process.

Figure 16: Fickle Stealer’s execution flow

Initially, Fickle Stealer creates a mutex to prevent a race condition. It then performs a series of anti-analysis checks and exits the process while it is being analyzed. Generally, it shows a fake error message before terminating the process. 

The fake error message. It reads: Unknown error occurred. Code: 0x55555

Figure 17: The error message

Below are the anti-analysis techniques used:

  • BeingDebugged Flag
    Parses the Process Environment Block (PEB) structure to check the BeingDebugged flag at offset 0x2. When the flag is set, which means it’s being debugged, Fickle Stealer exits the process without popping out a fake message.
  • Currently running processes
    Compares process names to names of analysis tools and some keywords that can be used in an analysis environment

Query string:
SELECT Name FROM Win32_Process

tcpview, wireshark, fiddler, procexp, autoit, df5serv, OllyDbg, x64dbg, x32dbg, WinDbg, fakenet32, fakenet64, ProcessHacker, autorunsc, filemon, procmon, regmon, idaq, idaq64, ImmunityDebugger, dumpcap, HookExplorer, ImportREC, PETools, LordPE, SysInspector, proc_analyzer, sysAnalyzer, sniff_hit, joeboxcontrol, joeboxserver, ResourceHacker, Fidder, httpdebugger, PE-bear, die, sample, malware, virus, sandbox, maltest, test, and virustest

  • Loaded module
    When a file is running in a sandbox, corresponding Dynamic-link library (dll) files are loaded to help with analysis. Fickle Stealer calls the GetModuleHandleW function to check whether any are loaded to memory. 

SbieDll, SxIn, Sf2, snxhk, cmdvrt32

  • Virtual machine
    The results of querying the following WMI objects are null in some virtual machines.

Query string:
SELECT * FROM Win32_PortConnector
SELECT * FROM CIM_PhysicalConnector
SELECT * FROM Win32_MemoryArray
SELECT * FROM Win32_MemoryDevice
SELECT * FROM Win32_PhysicalMemory
SELECT * FROM Win32_CacheMemory

Blacklist: (Empty)

Comparison between the results in querying a series of WMI objects on a virtual machine and a real environment. The objects are null in the virtual machine but return results in the real environment.

Figure 18: The result is null in some virtual machine

  • Hardware ID
    Compares hardware ID to IDs that might have been used in analysis environments.

Query string:
SELECT UUID FROM Win32_ComputerSystemProduct


  • User name
    Calls the GetEnvironmentVariableW function and compares the result to names that might have been used in analysis environments.

Billy, george, Abby, Darrel Jones, John, John Zalinsk, John Doe, SHCtAGa3rm, UV0U6479boGY, 8wjXNBz, WALKER, oxYT3lZggZMK, t3wObOwwaW, uh6PN, sMdVVcp, 06AAy3, mLfaNLLP, JPQlavKFb0Lt0, 7HV8BUt5BIsCZ, aFgxGd9fq4Iv8, Frank, Anna, wdagutilityaccount, WDAGUtilityAccount, hal9th, virus, malware, sandbox, sample, currentuser, emily, hapubws, hong lee, jaakw.q, it-admin, johnson, miller, milozs, microsoft, sand box, and maltest.

Next, it creates a new folder in the Temp folder with a random name, drops its copy to the new folder, and executes the copy. The currently running stealer will be terminated, and the copy will finish the remaining work to communicate with the server and send stolen data to the server.

Diagram illustrating the communication between the server and the Fickle Stealer. The stealer sends the victim information to the server, the server sends back a target list, and the stealer sends back the stolen data.

Figure 19: Communication between the server and Fickle Stealer

If the environment check is passed, Fickle Stealer sends victim information to the server. The server sends a list of target applications and keywords as a response. Fickle Stealer sends all files in folders according to the list. The stolen data is stored in a specific JSON format that has three key-value pairs:

{“name”: “RB_{Computer name}”,
“title”: {File name},
“body”: {File content}}

In this sample, its name contains a string RB and the name of the victim’s computer. In version 1.5.7 (a641d10798be5224c8c32dfaab0dd353cd7bb06a2d57d9630e13fb1975d03a53), the string “RB” is changed to “Hold.” The title indicates the data it grabs. It usually contains a tag followed by a file path. The body is base64-encoded file content. After being compressed with the Deflate algorithm, the JSON-formatted data is sent to the server. There are some exceptions. For example, the first packet sent to the server contains the following items, and the title is System.txt.

user name, user domain, DNS host name, NetBIOS name, screen resolution, OS version, language, host name, ip address and hardware information: CPU, GPU, Antivirus software, installed application and currently running process
Example first data packet with user information

Figure 20: The data in the first packet

The server’s response is also in JSON format and has three key-value pairs: status, k, and c. The target list, encrypted using an RC4 algorithm and then base64 encoded, is stored in c. The decryption key for RC4 is stored in k, as the following image shows.

Example response from the server with target list

Figure 21: The response from the server

There are four kinds of targets: crypto wallets, plugins, file extensions, and partial paths. Below are the targets specified by the server and the way the data is processed:

WalletSends files in specified folders. The title of data to send has a “wallet::” tag.AtomicWallet, Exodus, JaxxWallet, Electrum, ByteCoin, Ethereum, Guarda, Coinomi, Armory, ZCash
PluginSends files in specified folders. The title of data to send has a “plugin___” tag.Authenticator, EOSAuthenticator, Bitwarden, KeePassXC, Dashlane, 1Password, NordPass, Keeper, RoboForm, LastPass, BrowserPass, MYKI, Splikity, CommonKey, ZohoVault, NortonPasswordManager, AviraPasswordManager, TrezorPasswordManager, MetaMask, TronLink, BinanceChain, Coin98, iWallet, Wombat, MEWCX, NeoLine, TerraStation, Keplr, Sollet, ICONex, KHC, TezBox, Byone, OneKey, DAppPlay, BitClip, SteemKeychain, NashExtension, HyconLiteClient, ZilPay, LeafWallet, CyanoWallet, CyanoWalletPro, NaboxWallet, PolymeshWallet, NiftyWallet, LiqualityWallet, MathWallet, CoinbaseWallet, CloverWallet, Yoroi, Guarda, EQUALWallet, BitAppWallet, AuroWallet, SaturnWallet, RoninWallet, Exodus, MaiarDeFiWallet, Nami, Eternl, UniSatWallet
File extensionSearches files with the following extensions in %USERPROFILE% and the sub-folder. The title of data to send has a “grabg::” tag..txt, .kdbx, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .odt, .odp, wallet.dat
Partial pathConcatenates %APPDATA% and the following strings and searches log files and ldb files in the Local Storage/leveldb subfolder. The title is “discord_dblist.txt”discord
Google/Chrome/User Data/Default
Yandex/YandexBrowser/User Data/Default
Microsoft/Edge/User Data/Default
BraveSoftware/Brave-Browser/User Data/Default
Google/Chrome SxS/User Data
Google/Chrome/User Data/Profile 1
Google/Chrome/User Data/Profile 2
Google/Chrome/User Data/Profile 3
Google/Chrome/User Data/Profile 4
Google/Chrome/User Data/Profile 5
Google/Chrome/User Data/Profile 6
Google/Chrome/User Data/Profile 7
Google/Chrome/User Data/Profile 8
Google/Chrome/User Data/Profile 9
Opera Software/Opera Stable
Opera Software/Opera GX Stable
Amigo/User Data
Torch/User Data
Kometa/User Data
Orbitum/User Data
CentBrowser/User Data
7Star/7Star/User Data
Sputnik/Sputnik/User Data
Vivaldi/User Data/Default
Epic Privacy Browser/User Data
uCozMedia/Uran/User Data/Default
Iridium/User Data/Default

Additionally, some applications are targets by default. Below are those targets and the way data is processed:

ApplicationsSends files in specified folder to the server. Most often, the tag is the application name in lower case appended by two colons:Anydesk, Ubisoft (tag:uplay::), Steam, Skype, Signal, ICQ, Filezilla, Telegram, Tox, Pidgin, Element
Gecko engine browserSearches %APPDATA%, %LOCALAPPDATA% or %USERPROFILE% for these files: logins.json key4.db, keydb (tag: geckologins::) and  cookies.sqlite (tag: geckocookies::)If found, it copies the file to the Temp folder, sends a copy to the server, and deletes the copy.
Chromium based browserSearches “os_crypt” and “encrypted_key” in the Local state file to get a decryption key. It parses data in Cookies, History, WebData, and Login Data files to obtain sensitive data and sends a summarized result to the server. These files are also copied to the Temp folder before Fickle Stealer reads them. They are later deleted. The title is browser and the data is stored in JSON format.
Example stolen data from Opera containing user login data

Figure 22: The data from Opera. Each browser can have different content

Finally, it sends a screenshot to the server and deletes itself by executing the following command:

cmd.exe /c timeout /t 5 & del /f /q {stealer} && exit


In addition to some popular applications, this stealer searches sensitive files in parent directories of common installation directories to ensure comprehensive data gathering. It also receives a target list from the server, which makes Fickle Stealer more flexible. Variants receiving an updated list are observed. The frequently updated attack chain also shows that it’s still in development. FortiGuard will continue monitoring malware variants and provide appropriate protections as needed.

Fortinet Protections

The malware described in this report is detected and blocked by FortiGuard Antivirus as:




FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each of these solutions. As a result, customers who have these products with up-to-date protections are protected.

The FortiGuard CDR (content disarm and reconstruction) service, which runs on both FortiGate and FortiMail, can disarm the malicious macros in the document.

We also suggest that organizations go through Fortinet’s free NSE training module: NSE 1 – Information Security Awareness. This module is designed to help end users learn how to identify and protect themselves from phishing attacks.

FortiGuard IP Reputation and Anti-Botnet Security Service proactively block these attacks by aggregating malicious source IP data from the Fortinet distributed network of threat sensors, CERTs, MITRE, cooperative competitors, and other global sources that collaborate to provide up-to-date threat intelligence about hostile sources.

To stay informed and proactively defend against attacks like Fickle Stealer, sign up to receive Outbreak Alerts from Fortinet.

If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.


IP Addresses




 hxxps:// github[.]com/SkorikJR

























































Don’t Stop Here

More To Explore

favicon__1_ removebg-png


Stay informed with the latest insights in our Infostealers weekly report.

Receive immediate notification if your email is involved in an infostealer infection.

No Spam, We Promise

favicon__1_ removebg-png


Stay informed with the latest insights in our Infostealers weekly report.

Receive immediate notification if your email is involved in an infostealer infection.

No Spam, We Promise