If you have used a private key generated on WalletGenerator.net after August 17, 2018, move your funds immediately to a secure address.
- Who is affected: Anyone who has put funds in a public / private key generated via WalletGenerator.net after August 17, 2018.
- When: August 17, 2018 — ???. While the malicious behavior is not presently found as of May 24, 2019, it could be reintroduced at any point.
- What happened: There were changes to the code being served via WalletGenerator.net that resulted in duplicate keypairs being provided to users. These generated keypairs were also potentially stored server-side.
- What you should do if you are affected: Securely create a new keypair / wallet and move your funds to that new, secure address. Some folks have recommended using bitaddress (offline) via https://github.com/pointbiz/bitaddress.org.
We were able to contact the current owner of the site prior to publishing this post and outlined some of our findings in the hopes they would secure the server and help with the investigation. They responded by stating that they were unable to verify our claims and asking if we were perhaps on a phishing website.
Sometime between the time we were last investigating and testing (the evening of May 22, 2019) and the time we received an email response from the current site owner (midday, May 23, 2019), the code being served to the site was modified to remove the previously-added, malicious, code.
In this strange turn of events, we still have no idea whether the current site owner is the malicious party, if the server is insecure, or both.
We’re still considering this highly suspect and still recommending users who generated public / private keypairs after August 17, 2018, to move their funds. We do not recommend using WalletGenerator.net moving forward, even if the code at this very moment is not vulnerable.
The Long Version
Paper wallet interfaces are a super useful and convenient tool for users to easily generate a private / public keypair though, historically, these interfaces have been susceptible to vulnerabilities in the RNG / key derivation due to malicious or ignorant behavior conducted internally by the site owners or externally by bad actors. If the random number generator is compromised in any way, it can result (and has resulted) in guessable secrets which can, in turn, result in user funds being stolen.
This is what has happened with WalletGenerator.net.
Details of the Compromise
WalletGenerator is a website that generates paper wallets for a handful of different cryptocurrencies. The code served to WalletGenerator.net is intended to be open-source and audited, and (supposedly) matches the code here: https://github.com/walletgeneratornet/WalletGenerator.net.
This project changed ownership approximately two years ago. It was recently brought to our attention that the code being served via the WalletGenerator.net URL did not match the code on GitHub.
At this time, the code on GitHub is not malicious nor vulnerable, nor has it been malicious or vulnerable previously.
It is unclear at this time if the new owner is responsible for these code changes or if the server has been compromised by an external party.
The Code Changes
We investigated the differences by running a diff between the GitHub code and the server code and noticed (among other things) that an XHR request is being performed to grab the coin image. This is strange because the coin image is already downloaded by your browser when you load the HTML page—there should be no need to request it again.
We looked at the code to see why it’s sending an XHR request, and what we found was bizarre. It’s using the image data to seed the random number generator function.
This immediately raised eyebrows because the SecureRandom function should be taking input from the user’s browser / interactions, not from an image or server. Seeding it with data from the server compromises the integrity of the randomness, potentially making the keys generated non-random or producible.
ELI5: When generating a key, you take a super-random number, turn it into the private key, and turn that into the public key / address. However, if the “super-random” number is always “5,” the private key that is generated will always be the same. This is why it’s so important that the super-random number is actually random…not “5.” 😉
Additionally, a newSecureRandomAdvanced function was added that modifies (it removes poolCopyOnInit logic) the previously-used SecureRandom of bitaddress.org function. The output from this SecureRandomAdvanced function replaces theSecureRandom function. Even though the user is still prompted to move their mouse around the screen, this data is never used to seed the key generation.
Back to the Weird Image Request
Diving deeper into the image file itself, we noticed that the file was unusually large and producing a different sha256sum for different parties. At this point, it appears the image being served by the server was 1) unique for each user (IP address?) and 2) being used to seed the key generation.
We spun up a couple of VPNs and hit the domain from various geographical locations. Below is a dump of the sha256sum hashes of the file bitcoin.png:
$ sha256sum bitcoin.png (United Kingdom) 27cfafd3fe3810a89375a2f3ccc253cd6b2f03b5ff30ec6b41a76f8f2393085d local.png $ du -hs bitcoin.png 156K bitcoin.png $ sha256sum bitcoin.png (Netherlands) 4798d4167a98b56dc112878aed578f64ff9fb20fc58774a468e9b53f9aa1fc59 nl.png $ du -hs bitcoin.png 16K bitcoin.png $ sha256sum bitcoin.png (California) 4798d4167a98b56dc112878aed578f64ff9fb20fc58774a468e9b53f9aa1fc59 na_cali.png $ du -hs bitcoin.png 16K bitcoin.png $ sha256sum bitcoin.png (N. Virginia us-east-1) 86b475b38b137e50e317ce4478cc9abf41d33c158e12d2174dc1dd6f786ec45f onvpn.png $ du -hs bitcoin.png 156K bitcoin.png $ sha256sum bitcoin.png (Spain) 4798d4167a98b56dc112878aed578f64ff9fb20fc58774a468e9b53f9aa1fc59 offvpn.png $ du -hs bitcoin.png 16K bitcoin.png
This led us to a conclusion that these images are served to a deterministic percentage of the visitors, likely based on the IP.
The bitcoin.png with the hash 479...c59 and a file size of 16K is the unmodified bitcoin.png icon. The other hashes and file with a file size of 156K are modified in some way and likely stored on the server in order to re-generate the keypairs at a later date and steal funds.
Generate All the Keys
Approaching from a different angle, we then used the “Bulk Wallet” generator to generate 1,000 keys. In the non-malicious, GitHub version, we are given 1,000 unique keys, as expected.
However, using WalletGenerator.net at various times between May 18, 2019 — May 23, 2019, we would only get 120 unique keys per session. Refreshing our browser, switching VPN locations, or having a different party perform the same test would result in a different set of 120 keys being generated.
In the above video, you’ll see the XHR request. We go through the seeding process and then generate 1,000 keys. Everything appears to be fine on the surface. However, when we copy the output into a text editor, remove the indexes (replacing \d+, with an empty string), and sort the lines, we see the groups of duplicate keys.
We have determined that all the keys generated are fully deterministic using the image that was grabbed via the new XHR request to seed the RNG. If you use the same image as the seed at a later date, it will generate those same 120 keys. How exactly the image is manipulated server-side and when the malicious image is served remains unknown.
The image payload is visually identical for all the visitors, but the content is generated by the server — the binary content for the image—is usually different for each user, although sometimes it can be same for the same user / session. For example, if I have an image with the hash 27fc...085d then come back later to generate more keys, I will have the same malicious image payload and therefore the same keys. This may have to do with either client-side or server-side caching, as using a VPN or switching browsers will result in a new image and new set of keys being generated.
We wanted to know what was being inserted into the image, so we used binwalk to extract the data (see the Zlib data). This is a legitimate way to construct a PNG image, and we suspect there is some steganography happening to make the image visually identical but having different bytes to each user.
We know something is happening to manipulate the image, but are unable to determine what exactly is happening. Most steganography tools include a password functionality, meaning successfully decrypting the “hidden” data is impossible without knowing the password provided at the time of the image’s construction.
Furthermore, the manipulation may be random, with the images simply being stored on the server for re-use at a later date.
- What determines whether or not you are served the malicious version of the site / image?
- How the malicious image is generated and what data is added? Is it random or known data (IP address, timestamp)?
- Who made these changes? The current site owner? A malicious party who gained access to the server?
- 2019-05-17—2019-05-20: Investigation and confirmation of malicious behaviour.
- 2019-05-17—2019-05-22: Consultations with industry security experts on how best to handle the situation to minimize loss of user funds.
- 2019-05-22: Notified current site owner via email.
- 2019-05-23: Received (unhelpful) response from current site owner.
- 2019-05-24: Public disclosure.
- Thanks to PhishFort for funding addresses to see if they are auto-swept!
- Thanks to everyone who provided insights, advice, and confirmation of our initial assumptions.
- Thanks to MyCrypto for giving me the ability to investigate these types of situations and providing necessary support and resources.
- This post will be updated if / when new information comes to light. It was last modified 2019–05–24 @ 5:30am PT.
If you have any questions or can provide more insight, please reach out to me directly at email@example.com.