($200 reward) Phantom Wallet seed phrase recovery
#1
Hey!

I use the Phantom wallet extension (a Solana crypto wallet) but it got suddently corrupted and now when i try to login using my password, which is written since a long time on a text file on my computer, i have this error message "Something went wrong, please try again later". 

I assume that the extension got corrupted (maybe after some clearing cache, idk) and the best and quickest way to recover the wallet is using my seed phrase but since i wasn't expecting that at all, i still haven't backed up it and i need to find an other solution.

After some research, i found from a reliable source that Phantom wallet store the seed phrase locally (i use Windows 10) on the extension files (a .ldb file) and is encrypted by the wallet password (that i have). 

I found my encrypted seed phrase, it looks like this : 

{"encryptedKey"{"digest":"sha256","encrypted":"wFCwaUXv2nCNS1vBcuZiXnhS1hNK2e5ZerPTWz1Nst37x64WQBmMS1QWuetdp3X4P","iterations"10000"kdf":"pbkdf2","nonce":"2AeSHJN3yAviUqmg3AGqRjdZEAjX7mAq4","salt":"q9FJnyK8eaQPEz7XsNgUT"},"version":1}
(PS : it is an encrypted seed phrase from a new Phantom wallet with no funds on it)

After some other research, i heard about the "Metamask vault decryptor", which is a tool that can decrypt those type of encrypted data by using the wallet password but unfortunately it works only with Metamask wallet.

It's for this i'm here now to ask if there is a gigabrain that know a tool or a script to decrypt this gibberish? The wallet contain $2k, which is precious money for me. I promise a $200 reward for the person who helped me to decrypt this.

(PS : if that can help, i assume that the encryption and decryption code for Phantom wallet is quite similar to the code that you can find on this article : https://medium.com/@alevymyers/recoverin...a59e61ae08)

If you need any extra infos, feel free to ask.

Nice greetings!
Reply
#2
any update on this?
Reply
#3
(02-01-2024, 07:46 PM)Qwarkz Wrote: {"encryptedKey"{"digest":"sha256","encrypted":"wFCwaUXv2nCNS1vBcuZiXnhS1hNK2e5ZerPTWz1Nst37x64WQBmMS1QWuetdp3X4P","iterations"10000"kdf":"pbkdf2","nonce":"2AeSHJN3yAviUqmg3AGqRjdZEAjX7mAq4","salt":"q9FJnyK8eaQPEz7XsNgUT"},"version":1}
(PS : it is an encrypted seed phrase from a new Phantom wallet with no funds on it)

I may take a look at this. Do you remember the password you used for this example wallet? ^
Reply
#4
Research dump for whoever picks this up (Potentially me)

It appears to be PBKDF2_SHA256 KDF of the password then using xsalsa20-poly1305 for the encryption/decryption using tweetnacl's "secretbox" function

https://github.com/project-serum/spl-tok...#L124-L130

Will likely be quite a large effort to implement this, as xsalsa20-poly1305 hasn't been implemented before and the Phantom codebase is a mess
Reply
#5
(04-04-2024, 05:58 AM)penguinkeeper Wrote: Research dump for whoever picks this up (Potentially me)

It appears to be PBKDF2_SHA256 KDF of the password then using xsalsa20-poly1305 for the encryption/decryption using tweetnacl's "secretbox" function

https://github.com/project-serum/spl-tok...#L124-L130

Will likely be quite a large effort to implement this, as xsalsa20-poly1305 hasn't been implemented before and the Phantom codebase is a mess

Thanks for the reply penguinkeeper. Given the encrypted seed phrase, is there an easy way to find the public key? That would help me confirm how much funds are on the wallet so I can offer a bounty as well that could incentivize the effort needed to implement this. Thank you, and I hope you keep looking into this.
Reply
#6
No, it'd have to be cracked first so the private/public key can be derived from the phrase. What was the password to the above example you provided?
Reply
#7
hi, I just solved this --- i don't check this forum, but feel free to add me on discord if you don't hear back..
here's how you can fix it.

the first script will give you a 128 byte string, which you can then convert into a 12 word mnemonic with the 2nd script and import into your wallet.


tips appreciated: F8UghnajaBdfj5cNBSY8ztGg4EfnaY6XJayBPZiJTpm9 (SOL) Smile -- this 100% works, ping me on discord or here if you have trouble.

discord user: o7_


import base58
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from nacl.secret import SecretBox
from nacl.exceptions import CryptoError
# Encrypted data and parameters (assuming all are base58 encoded)
data = {
    "encrypted": "",
    "nonce": "",
    "salt": "",
    "iterations": 10000,
}
password = ''  # Replace with your actual password

# Decode from Base58
salt = base58.b58decode(data['salt'])
nonce = base58.b58decode(data['nonce'])
encrypted_data = base58.b58decode(data['encrypted'])
# Derive the key using PBKDF2 with SHA-256
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=data['iterations'],
    backend=default_backend()
)
key = kdf.derive(password.encode())
# Decrypt the data using PyNaCl's SecretBox
box = SecretBox(key)
try:
    decrypted = box.decrypt(encrypted_data, nonce)
    # Instead of attempting to decode as UTF-8, print the raw bytes or their hex representation
    print("Decrypted data (hex):", decrypted.hex())
except CryptoError as e:
    print(f"Decryption failed: {e}")

##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################
##################SECOND SCRIPT BELOW##############################

from mnemonic import Mnemonic
import binascii
# Decrypted data in hex
decrypted_hex = 'your_128_byte_string'
# Convert hex to bytes
entropy_bytes = binascii.unhexlify(decrypted_hex)
# Generate mnemonic from the entropy bytes
mnemo = Mnemonic("english")
mnemonic = mnemo.to_mnemonic(entropy_bytes)
print("Mnemonic:", mnemonic)
Reply
#8
(04-04-2024, 05:58 AM)penguinkeeper Wrote: Research dump for whoever picks this up (Potentially me)

It appears to be PBKDF2_SHA256 KDF of the password then using xsalsa20-poly1305 for the encryption/decryption using tweetnacl's "secretbox" function

https://github.com/project-serum/spl-tok...#L124-L130

Will likely be quite a large effort to implement this, as xsalsa20-poly1305 hasn't been implemented before and the Phantom codebase is a mess

(04-07-2024, 08:25 PM)tachsahtac Wrote:
(04-04-2024, 05:58 AM)penguinkeeper Wrote: Research dump for whoever picks this up (Potentially me)

It appears to be PBKDF2_SHA256 KDF of the password then using xsalsa20-poly1305 for the encryption/decryption using tweetnacl's "secretbox" function

https://github.com/project-serum/spl-tok...#L124-L130

Will likely be quite a large effort to implement this, as xsalsa20-poly1305 hasn't been implemented before and the Phantom codebase is a mess

Thanks for the reply penguinkeeper. Given the encrypted seed phrase, is there an easy way to find the public key? That would help me confirm how much funds are on the wallet so I can offer a bounty as well that could incentivize the effort needed to implement this. Thank you, and I hope you keep looking into this.

(04-08-2024, 05:04 AM)penguinkeeper Wrote: No, it'd have to be cracked first so the private/public key can be derived from the phrase. What was the password to the above example you provided?


check my previous reply Smile --- thanks penguin for the xsalsa callout -- it helped tremendously
Reply
#9
import base58
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from nacl.secret import SecretBox
from nacl.exceptions import CryptoError

data = {
    "encrypted"""# Replace with your actual encrypted data
    "nonce""",# Replace with your actual nonce
    "salt"""# Replace with your actual salt password
    "iterations"10000,
}
password = ''  # Replace with your actual password

# Decode from Base58
salt = base58.b58decode(data['salt'])
nonce = base58.b58decode(data['nonce'])
encrypted_data = base58.b58decode(data['encrypted'])

# Derive the key using PBKDF2 with SHA-256
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=data['iterations'],
    backend=default_backend()
)
key = kdf.derive(password.encode())

# Decrypt the data using PyNaCl's SecretBox
box = SecretBox(key)
try:
    decrypted = box.decrypt(encrypted_datanonce) # Your private key in hex string format
except CryptoError as e:
    print(f"Decryption failed: {e}")

# Convert hex to bytes
entropy_bytes = binascii.unhexlify(decrypted.hex()))

# Generate mnemonic from the entropy bytes
mnemo = Mnemonic("english")
mnemonic = mnemo.to_mnemonic(entropy_bytes)
print("Mnemonic:"mnemonic)


Made it just one script which will print your Mnemonic -- Remember though, even the decrypted hex string is sensitive.
Reply
#10
(04-19-2024, 08:42 AM)Markelov69 Wrote: Thank you for your response, penguinkeeper. Is there a straightforward method for locating the public key with the encrypted seed phrase? This would assist me in verifying the amount of funds in the wallet, enabling me to offer a bounty that could motivate the effort required to implement this. Your continued exploration of this matter is appreciated.
I already addressed this previously - it isn't possible. Also, is the solution above not fit for your purpose?
Reply