Extracting the Jaxx 12-word wallet backup phrase
Contents
Updates
Because this matter is still ongoing (Jaxx does not seem to want to fix this vulnerability), I have moved the updates here to the front. The original post is below.
2022-08-14
In order to help a reader who was having trouble with nodejs, I added a section below with a replit setup that you can run with the click of a button.
2018-07-19
Reader Alex points out in the comments that newer versions of Jaxx use a different storage method, and links to this LinkedIn article.
It seems that newer versions of Jaxx use leveldb instead of the old sqlite format databases. The pin can still by quite easily extracted, as shown by the LinkedIn article, but I don’t yet know if the 12-word phrase can also still be extracted.
If I can make some time, my plan would be to use something like leveldb-json to dump the contents of the leveldb file, and then to analyse that for extraction possibilities.
2017-08-08 18:42 UTC
I have added the exact filesystem locations / paths to the relevant Jaxx local storage file to the demonstration section.
2017-06-20 07:51 UTC
Since the first publication of this post, Jaxx has publically stated several times that storing our wallets unsecurely is not a problem.
If that is indeed the case, why do all other reputable desktop wallets perform this encryption in the correct manner, thus safeguarding our wallets, and only Jaxx does not?
- Desktop wallets that DO CORRECTLY ENCRYPT your wallet: Exodus, MyEtherWallet, geth, parity, electrum.
- Desktop wallets that DO NOT CORRECTLY ENCRYPT your wallet: Jaxx.
(Jaxx “encrypts” the wallet seed, but with a hard-coded and easily extracted key, which means this is not encryption but rather obfuscation, which is not much better than no encryption.)
2017-06-13 10:14 UTC
Reader Imed reports in the comments below that the 4-digit user PIN is stored as an unsalted sha256 hash, which can easily be reversed using rainbow tables, for example via sites like CrackStation.
I have just confirmed with a test Jaxx installation that I am able to extract a configured PIN from the local storage database without Jaxx running of course.
2017-06-11 10:08 UTC
Daira Hopwood correctly points out in the comments that encrypting using the PIN would be too easily brute-forced. I have updated the post in two places to indicate that instead Jaxx does in fact need to implement support for a strong password. One can discuss whether to do this differently for the desktop (no sandboxing) than for mobile devices (usually good sandboxing).
2017-06-10 20:19 UTC
Based on this response by the Jaxx CTO on reddit, they are not planning to fix this vulnerability. If that is the case, I strongly recommend that you avoid the Jaxx wallet.
Introduction
I was curious how easy it would be to extract the 12-word wallet backup phrase from a Jaxx cryptocurrency wallet desktop app / chrome extension install.
After an hour or two of analysis, I can conclude that this is unfortunately far too easy.
Even when your Jaxx has a security PIN configured, anyone with 20 seconds of (network) access to your PC can extract your 12 word backup phrase and copy it down. Jaxx does not have to be running for this to happen.
With the 12 word backup phrase, they can later restore your wallet, including all of your private keys, on their own computers, and then proceed to transfer away all of your cryptocurrency.
The main problem is that the Jaxx software encrypts the mnemonic using a hard-coded encryption key, instead of making use of a strong user-supplied password. (As Daira Hopwood points out in the comments, using the PIN would not be sufficient.)
This means we can easily read and decrypt the full recovery phrase from local storage using sqlite3 and some straight-forward code.
I successfully tested this vulnerability on the Jaxx Chrome extension v1.2.17 and the Jaxx Linux desktop app 1.2.13.
Demonstration
To test this proof of concept, you will need node.js
installed. Ensure that your Jaxx is PIN protected, just for fun. It
won’t help.
On Linux or Mac, open the Jaxx local storage file using the
sqlite3
tool, or if you prefer GUIs you can use sqlitebrowser. You
can find this file at the following locations depending on your
operating system, and whether you’re using the desktop app or the
chrome extension:
- Linux desktop:
$HOME/.config/Jaxx/Local\ Storage/file__0.localstorage
- Linux chrome extension:
$HOME/.config/google-chrome/Default/Local Storage/chrome-extension_ancbofgphhmhcchnaognahmjfajaecmo_0.localstorage
- macOS desktop:
/Users/[username]/Library/Application Support/Jaxx/Local Storage/file__0.localstorage
, thanks to Manuel in the comments; - Windows desktop:
C:\Users\<Your Computer's User Name>\AppData\Roaming\Jaxx\Local Storage
- Windows chrome extension:
C:\Users\<Your Computer's User Name>\Local\Google\Chrome\User Data\Default\Local Storage\chrome-extension_ancbofgphhmhcchnaognahmjfajaecmo_0.localstorage
At the sqlite3
prompt, do the following:
|
|
(If you opted for sqlitebrowser, just copy out the value of the
mnemonic
key.)
Note the returned value down. This is Jaxx’s encrypted mnemonic which we shall decrypt into your 12 word backup phrase.
(If the returned string is too short in your case, try
sqlitebrowser
instead. In my case, sqlite3
works perfectly for
the desktop Jaxx, but not the Chrome Jaxx, where I use either the
chrome Dev Tools or sqlitebrowser
to extract the string.)
Install crypto-js version 3.1.2 by doing either npm install crypto-js@3.1.2
or yarn add crypto-js@3.1.2
, and then run the
following code using node
, after substituting the
mnemonicEncrypted
variable value with the one you extracted using
sqlite3
:
|
|
This should print out your 12 word backup phrase, in the case of this dummy setup I’m seeing “snake purity emerge blue subway lab loyal timber depth leg federal work” which is indeed correct.
A more straight-forward demonstration using replit
If you don’t have or want to install nodejs, you can use this replit that I made.
Click on the “play” button to see it spit out the decrypted demonstration mnemonic.
To try it out on your own encrypted mnemonic, fork that replit, set your own
encrypted mnemonic on the var mnemonicEncrypted =
line, and click on the “play”
button.
How can we fix this?
The thing is, Jaxx is unfortunately one of the better cross-platform multi-currency wallets. Although it has a great UI, I personally don’t like Exodus, because they don’t let me manage more than one Ethereum address.
To mitigate the Jaxx security issue discussed here, keep the Jaxx
desktop app’s local storage directory on an encrypted filesystem
which you only mount when you’re using Jaxx, and unmount directly
afterwards. This is what I’m currently doing using encfs
.
If you prefer using the Chrome extension, you can try symlinking
just the extension’s local storage file as it lives in Chrome’s
global Local Storage
directory.
Importantly, keep on encouraging Jaxx support to add support for using a strong user-supplied password as part of the encryption key (just like Exodus) with which they encrypt your mnemonic (recovery phrase) and all other sensitive values in local storage. Refer them to this post for more details. (See Daira Hopwood’s comment, using the PIN for encryption is not sufficient.)
P.S.
If this helped you, and you like sending ethereum around, feel free to
send some to address 0xA3448C2e3F22F58759fd5dD14BE76269034d440E
also
known as the following QR code: