What causes a man to wake up one day and say, “I’m going to build my own malware and go sell it to cybercriminals on the dark web”? After all, the market is saturated with competitors, and the product is judged on the one sole metric of how many victims it has successfully parted with their funds and personal data. Statistically, during the past 5 years, someone must have created what would have been the great malware strain to stun the entire industry, but the first two criminals to actually try out the thing had a weak spam game, got weak results, and that was that.
All this must have been acutely clear to an individual who on September 24, 2022, under the alias “kingcrete2022”, posted the following to the appropriate channels:
The author did not rush into this venture. They had already spent half a year lurking in the forum as “kingcrete2022”, and possibly more than that under other aliases. Builds of the malware would later surface that were already polished enough to see the light of day, yet had been compiled a full month before the official launch. It would not be surprising if this person had already spent a long time operating in the cybercrime sphere, even before the debut of the “King Crete” persona – earlier this year Analyst1 published an astoundingly in-depth report on the LockBit gang, with a repeated emphasis on how the ransomware-sphere was much smaller and incestuous than people tend to assume; it’s easy to imagine the same incentives at work dictating that one does not go from being a nobody to publishing an offering like the above after just six months of familiarity with the cybercrime landscape, and that this isn’t the King’s first rodeo.
Following this announcement, KingCrete aggressively went to work, determined to prove that his was the superior product. Curious would-be clients who nonchalantly remarked “seems interesting, I will check it out” earned an immediate response and a request to post a review and follow up with comments. A manic stream of version updates checkered the forum thread for the coming months, adding a very long litany of features, sub-features and sub-sub-features, and providing support in both English and Russian. One update reads, “repaired major security vulnerability that the panel session is not affected by password modification”; just “security vulnerability” alone would have technically sufficed, but it doesn’t project the same ruthless self-criticism and drive for excellence.
As luck would have it, the first few customers had their campaigns go as planned. “I like this stealer, rep++”, said one. “The stealer rocks, stealer and support is just great”, said the other. Just between these few early adopters, the campaigns they set up racked up thousands of compromised users, hundreds of thousands of compromised passwords and several hundred compromised cryptocurrency wallets. Some prospective customers had trouble getting their operation running, and the author made sure not to leave them behind, posting: “for those who have difficulties in purchasing VPS or servers, we provide turnkey solutions, please contact us if you need”.
Interested?
In theory, the author of Rhadamanthys isn’t concerned with what you do with the ill-gotten data handed to you by the stealer. Commit fraud, sell the data, start a civil war, it’s all the same to him. In practice, the main customers for off-the-shelf malware like this are opportunistic cybercriminals, who aim to infect whomever and whenever (“as long as the target is not located in the commonwealth of independent states”, per the author, who certainly did not invent that practice). Campaign victims are therefore spread all around the world, though some spikes are visible where a campaign particularly enjoyed success, and some operators will put their own twist on where and how they aim the infection (one campaign disseminated samples under the guise of video editing software, such as OBS studio, pushed to the crowd of unsuspecting streamers via Google ads).
Common wisdom says people who operate this sort of malware are typically not too concerned with “big game hunting” the way the big ransomware gangs are. To them it’s a numbers game: rake in many victims and monetize them wholesale. Think of your favorite hack of the past decade, and chances are at no point it came to light that actually the initial breach was due to a trigger-happy cybercriminal spamming Emotet maldocs in every direction, who then suddenly realized one of their twenty thousand victims was a lucrative target. Still, these indiscriminate attacks do end up aimed at major organizations, by sheer statistics; via our telemetry we were able to confirm an attempted Rhadamanthys infection of a government agency in Canada, as well as an energy company in India’s infrastructure sector. We like to imagine that even if these attempts had succeeded, they would have led to just two more people having a bad day and canceling their credit cards, and not the more troubling scenario of a hefty sum exchanging hands followed by a champagne bottle being opened someplace geopolitically hostile to India; but there is certainly no guarantee of it.
In the award-winning film Everything Everywhere All At Once, antagonist Jobu Tupaki delivers the following monologue:
I got bored one day, and I put everything on a bagel. Everything. All my hopes and dreams, my old report cards, every breed of dog, every last personal ad on craigslist. Sesame Poppy seed. Salt. And it collapsed in on itself. Because, you see, when you really put everything on a bagel, it becomes this. The truth. [..] Nothing matters. Feels nice, doesn’t it? If nothing matters, then all the pain and guilt you feel for making nothing of your life, it goes away. Sucked into a bagel.
Having read that, you now understand Rhadamanthys stealer’s design philosophy. The mind-numbing list of features included in the initial release speaks plenty for itself already, but we would be remiss not to emphasize the inclusion of – for instance – capabilities for stealing information from KMeleon and Pale Moon web browsers, which each possess a market share imperceptible to the naked eye; or stealing cryptocurrency from the Firefox Auvitas Wallet browser extension, which, as of the writing of these words, has exactly one user. Rhadamanthys’ feature list was not hand-picked to maximize return on developer time investment. It resulted from one simple guiding principle: “Add it in! Add it all in!”.
Maybe you are here for the actual list of things that King Crete had put on the bagel. In that pathological case, the list follows below, without bulleted list format so as not to consume too much vertical real estate. Rhadamanthys’ feature list includes stealing the victim’s system information – Computer name, username, ram capacity, CPU cores, screen resolution, timezone, geoip, environment, installed software, screenshots, cookies, history, autofill, saved credit cards, downloads, favorites and extensions; it steals credentials from FTP clients – Cyberduck, FTP Navigator, FTPRush, FlashFXP, Smartftp, TotalCommander, Winscp, Ws_ftp and Coreftp; and from Mail clients CheckMail, Clawsmail, GmailNotifierPro, Mailbird, Outlook, PostboxApp, Thebat!, Thunderbird, TrulyMail, eM and Foxmail; It steals credentials from 2FA applications and password managers RoboForm, RinAuth, Authy and KeePass; VPN services including AzrieVPN, NordVPN, OpenVPN, PrivateVPN_Global_AB, ProtonVPN and WindscribeVPN; Note-taking applications including NoteFly, Notezilla, Simple Stick Notes and Windows Sticky notes; Message history from messenger applications including Psi+, Pidgin, tox, Discord and Telegram; also, it steals victim credentials for Steam, TeamViewer and SecureCRT.
The author put a particular emphasis on features related to stealing cryptocurrency; in one version update, which featured 9 new features, 4 of these were enhancements to exfiltrating and cracking cryptocurrency wallets. The list of supported wallets in the initial release is truly unwieldy, and includes Auvitas, BitApp, Crocobit, Exodus, Finnie, GuildWallet, ICONex, Jaxx, Keplr, Liquality, MTV, Metamask, Mobox, Nifty, Oxygen, Phantom, Rabet, Ronin, Slope, Sollet, Starcoin, Swash, Terra, Station, Tron, XinPay, Yoroi, ZilPay, Coin98, Armory, AtomicWallet, Atomicdex, Binance, Bisq, BitcoinCore, BitcoinGold, Bytecoin, coinomi, DashCore, DeFi, Dogecoin, Electron, Electrum, Ethereum, Exodus, Frame, Guarda, Jaxx, LitecoinCore, Monero, MyCrypto, MyMonero, Safepay, Solar, Tokenpocket, WalletWasabi, Zap, Zcash and Zecwallet. We wouldn’t blame the reader for wondering how many of these are actually Pokemon that we had covertly added to the list as a practical joke.
All of these stealing actions are performed automatically upon infection. If the attacker decides to get more hands-on with the infected machine, they can push a new configuration to the “file grabbing” module, which will exfiltrate all files matching a windows search query; or, for the true power user, push hand-crafted powershell to be executed on the victim machine.
In this detailed and instructive write-up, Eli Salem punches with determination through each of the half-dozen execution stages (droppers, shellcodes, installers, …) that this malware goes through before it gets to the information-stealing functionality.
When analyzing Rhadamanthys, we have observed differences between the logic of the analyzed sample and the logic detailed in the above write-up, which testify to the malware’s constant development and flexible build process. Most notable was the behavior of the NSIS loader DLL, which in the execution flow we analyzed, creates a suspended process from C:\\Windows\\Microsoft.Net\\Framework\\v4.0.30319\\AppLaunch.exe
then replaces the suspended process’ sections one-by-one with injected malicious code.
As described in the above-mentioned write-up, the injected code then proceeds to load several execution stages in sequence, one of which attempts many VM evasions taken from the Al-Khaser project and then unhooks functions in ntdll.dll
in an attempt to avoid detection. Finally, it resolves an internally obfuscated C2 address and, from there, downloads the final stage containing the actual information-stealing functionality.
Analyzing the actual stealing logic is not so straightforward. Without access to a live C2 server, at this point an analyst has two options. Either they go chasing a brand new execution chain, doing the hard work debugging all the stages and hoping to catch a live C2 server which won’t filter them out using god knows how many heuristics; or else working with a dump in an unreadable state, obtained from a sandbox run that happened when the C2 was still live. In this particular case the memory dump contains many telling strings that telegraph what the malware does in broad strokes, but there are many obstacles before proper interactive disassembly can take place.
The first and most major obstacle is the lack of resolution for API calls. Opening the dump in a disassembler and following the function calls, one very quickly runs across what must be a homebrew import table of dynamically resolved functions. The dump is an artifact of a sandbox run that has long since concluded and these addresses seem to be meaningless now. We were able to resolve nearly every function using a method that will be explained below.
First, we know that these addresses, during the sandbox run, pointed to DLLs that were loaded into memory. Second, we know in what environment the execution took place: a tria.ge environment with the code name Win10v2004-20220812-en
. We upload our own dummy executable to the sandbox, make sure that we choose the same environment used in the original sandbox run, then look at a DLL of our choice and recover the DLL version.
Unfortunately, even if we do have the DLL version, Microsoft is not so generous with offering historical versions of DLLs for download. There are various workarounds for this sort of issue (e.g. you might want to consult winbindex). We opted to use an esoteric feature of tria.ge sandbox: many users had asked for functionality to manually dump files generated during an execution flow. As a workaround, the sandbox introduced a feature allowing users to dump any file they wish, as long as they open windows File Explorer and delete the file there manually. Well, if we try deleting kernel32.dll
from its residing place in C:\windows\system32
the OS won’t allow it (and justifiably so), but nothing prevents us from copying the file somewhere else, then deleting the copy. The same DLL loaded into memory during the original sandbox run is now available from the “downloads” section of the analysis report once the analysis is terminated.
In this way we download many DLLs that are the “usual suspects” that malware, or any software really, would want to resolve APIs from – such as advapi32.dll
, user32.dll
, msvcrt.dll
, ws2_32.dll
and so on. Now we can open each of these in a disassembler, which manually loads the file and assigns virtual addresses to each of the DLL functions. Sadly we are far from done because we still do not know the base address of the DLL when it was loaded originally, or even what particular DLL contains the function that some memory address refers to.
Not even knowing which is the relevant DLL can be mitigated to some degree by plain observation – for example, in the below image, the function qword_c5c08
(pointer value 0x7ffbf1bd5f20
) is taking a registry key as an argument, and so is highly likely to have come from advapi32.dll
. But this won’t work for every DLL – we won’t always be lucky enough to find a function that the malware feeds such an incriminating hardcoded string as a parameter. More crucially, even if we somehow knew the correct DLL for every function address, this still wouldn’t tell us the original address the DLL was loaded at during the original sandbox run, necessary to calculate the rebase delta between the function addresses that were loaded then into memory (which we are trying to resolve) and the labeled function addresses in the loaded, annotated DLL that we have open in the disassembler.
To get past this hurdle we note that the function addresses in the sandbox dump are probably divided into contiguous sequences that were each imported from the same DLL. This means if we take 10 qword pointers from the table and get lucky enough that they were all resolved from the same DLL, then in that DLL when loaded into memory, these 10 functions will exist with the same differences between their addresses. To show the key insight here we will use a toy example: suppose our list of 10 addresses to resolve begins with some address AX then proceeds with AX+0x300, AX+0x500, AX+0x930, and so on six other addresses; suppose further that in one of the loaded and annotated DLLs we find that for some address AY it happens that AY+0x300, AY+0x500, AY+0x930 and so on and so on are all addresses of functions. This is a very lucky coincidence to have happened on its own, and in all probability, the original address AX in the original sandbox run resolved to the function that is in AY in our annotated file. It is possible to further sanity-check the match by looking at the 10 function names that match the addresses on the list and verifying that they seem like a reasonable list to have been required by the software that had been run in the sandbox.
The following IDAPython code, when run in a loaded DLL database, will automate the task of finding matches of function address sequences:
exports = list(Functions(0x0000000000000000,0xFFFFFFFFFFFFFFFF)) def dll_match(imports): result = [] import_anchor = imports[0] for anchor in exports: if all([anchor+(_import-import_anchor) in exports for _import in imports]): result.append({_import:get_func_name(anchor+(_import-import_anchor)) for _import in imports}) return result
For example, the address seen in the above image (the one we suspect to have been resolved from advapi32.dll
) appears in the following sequence of 10 addresses:
[0x7ffbf1bd5950, 0x7ffbf1bd5f20, 0x7ffbf1bd6a80, 0x7ffbf1bd5f90, 0x7ffbf1bd6830, 0x7ffbf1bee0c0, 0x7ffbf1bee120, 0x7ffbf1bc42d0, 0x7ffbf1bdb970, 0x7ffbf1bd6780, 0x7ffbf1bd6c50, 0x7ffbf1bd69d0, 0x7ffbf1bd6490, 0x7ffbf1bd5f40, 0x7ffbf1bd7580, 0x7ffbf1bd7530, 0x7ffbf1bd6a20]
We open an annotated idb of the advapi32.dll
file we dumped from the sandbox, load the above IDA script and run the function dll_match
with this list of addresses as input. As output we receive the correct resolution for each of these function addresses.
It turns out that the above-mentioned function that had been loaded to the address 0x7ffbf1bd5f20
during the sandbox run is RegQueryValueExW
. Using this method, it is easy to go “shopping” and try running the same script against various DLLs to see what matches are obtained, and how feasible they are. While that specific workflow doesn’t scale very well, it’s not too difficult to see how the process can be streamlined, if need be (for instance, by keeping a precomputed database of function address differences of many DLL versions, and making all the difference comparisons against it).
Even with the API calls resolved, the database is still very large and spans over 2500 functions. Many of these are library functions from 3rd party libraries such as sqlite3 and lua_cjson; this introduces a further hassle in that resolving these functions requires us to compile our own annotated version of these libraries and then perform a bindiff (or somesuch) to label the functions used by Rhadamanthys. This is an infamously finicky process, and many of the labels are not of very much use before being verified manually.
With all that said, the state of the database is more palatable now and allows us to analyze the execution flow in a way we couldn’t before. As an example we will focus on the malware’s capability of stealing stored login credentials, cookies and so on from Google Chrome, which includes three stages:
The malware first performs a recursive search of the victim filesystem for a file named “web data” in order to navigate to %LOCALAPPDATA%\\Google\\Chrome\\User Data\\default
, then traverses the tree to look for other artifacts such as “Cookies” or “Login Data” and collects each match into a binary bitfield; if this bitfield is sufficiently nonzero, the malware is then satisfied that it has located the Chrome directory correctly.
The malware then accesses files of interest, such as login data
. Some of these files are SQL databases, in which case the malware initializes a SQL database from the file contents in-memory, then obtains the desired data by issuing a SELECT statement. In contrast, others are in JSON format, and so the malware instead calls a function to parse the JSON and extract the value associated with a certain key:
With the information parsed into plaintext format, it can now be appended to the database of stolen information that is eventually reported back to the attacker, and the stealing functionality for this specific target (Chrome) is concluded. Most of Rhadamanthys’ code base is composed of an entirely too large amount of variations on this same idea, each targeting a different piece of data as laid out in the earlier description of the malware’s feature set.
Rhadamanthys represents a step in the emerging tradition of malware that tries to do as much as possible, and also a demonstration that in the malware business, having a strong brand is everything. Some readers may recall the odd tale of Godzilla loader, which tried to undercut Emotet by retailing for a quarter of the price and boasted a set of features so different from its competitor that a proper comparison between the two was impossible. This was a stark demonstration that cybercriminals don’t make explicit calculations of which feature-sets will net them a higher amount of victims – they rely on a fuzzy feeling of how well they trust the developer, the brand and the sound of the feature list; and, failing that, on trial and error. Any developer can write a piece of malware, and some developers can even write a decent piece of malware with useful features, but it takes a cunning mind in tune with the market to come out swinging like the author of this malware did, shouting “I have all the features, I have the best features” and fostering a successful relationship with a base of early adopters.
Should we be worried? It is tempting to quietly classify malware like Rhadamanthys in the “nuisance” drawer. An uninvited credit card charge of $2,000 doesn’t seem like much compared to ransomware and the existential threat it poses to entire organizations. It’s easy to forget the process by which the $2,000 charge happens – the malware doesn’t just steal your credit card details, it steals everything. It is a relief that employees in government agencies and energy companies who get hit with this kind of malware are typically treated the same by attackers as any other victim, but we would do well to remember that this is not a law of nature.
We would also do well to dwell on the technical demands such malware poses to us on the defensive side. It used to be a quaint sport to sidestep sandboxes and frustrate analysts who are trying to properly disassemble, debug, analyze the API call log of some malware. For how long can the analysis be delayed? An hour? A day? God forbid, a week? But nowadays, there seems to be a sinister shift where malware authors have gotten more ambitious and are saying, let’s see how close I can get to the whole analysis just not happening. While we are still quite far off from that scary scenario, malicious instruments that inch us closer there are being thought of all the time, and the unexplored and under-explored space of such instruments is well and truly frightening. As an industry, it is paramount that we create new tools and protocols to meet these instruments with equal and opposite force when they arrive.