Pages:
Author

Topic: recover keys from wallet.dat without using pywallet - page 2. (Read 1651 times)

member
Activity: 180
Merit: 38
Code:
Private key encryption is done based on a CMasterKey, which holds a salt and random encryption key.

CMasterKeys are encrypted using AES-256-CBC using a key derived using derivation method nDerivationMethod (0 == EVP_sha512()) and derivation iterations nDeriveIterations. vchOtherDerivationParameters is provided for alternative algorithms which may require more parameters (such as scrypt).

Wallet Private Keys are then encrypted using AES-256-CBC with the double-sha256 of the public key as the IV, and the master key's key as the encryption key (see keystore.[ch]). Master key for wallet encryption
full member
Activity: 217
Merit: 109
When you create a recovered wallet... you have to specify a passphrase that the "recovered wallet.dat" is going to use... you then specify the "possible" passphrases it should try when recovering keys/data etc...

The script attempts to decrypt any encrypted keys found using the "possible" passphrases... and then puts them into the recovered wallet.dat, encrypted with a master key derived from the "recovered wallet.dat" passphrase (NOT the passphrase of the original wallet)
Thought so. I presume that while dumping private keys it wouldn't be impossible to show any decrypted master keys also. It would be a handy feature for any other ckeys that may share the same master key but were not gathered into the recovery.
HCP
legendary
Activity: 2086
Merit: 4363
When you create a recovered wallet... you have to specify a passphrase that the "recovered wallet.dat" is going to use... you then specify the "possible" passphrases it should try when recovering keys/data etc...

The script attempts to decrypt any encrypted keys found using the "possible" passphrases... and then puts them into the recovered wallet.dat, encrypted with a master key derived from the "recovered wallet.dat" passphrase (NOT the passphrase of the original wallet)
full member
Activity: 217
Merit: 109
When a recovered wallet is dumped with pywallet, is the master key near the end the encrypted version and if so, is it a result of the passphrase given for the recovered wallet? It would be great if the original decrypted master key, or master keys were listed, as I presume that possibly more than one original lot of wallet keys are gathered into the one recovered wallet. This would be handy for trying fragmented wallets where other ckeys were not gathered and processed.
HCP
legendary
Activity: 2086
Merit: 4363
The passphrase is no problem as I already know it. I wonder if the salt and iteration are viewable within the wallet file? How do these various scripts find them?
The "mkey" data stored in the wallet.dat is unpacked as follows:
Code:
encrypted_master_key, salt, method, iter_count = struct.unpack_from("< 49p 9p I I", mkey)
So, it's actually a 48 byte record called the "encrypted_master_key", the 8 byte salt, the 'method' (should be a 4 byte unsigned Int value 0) and the iteration count (4 byte unsigned Int).

Then, the 48 byte "encrypted_master_key", is actually parsed as:
Code:
iv = binascii.hexlify(encrypted_master_key[16:32])
ct = binascii.hexlify(encrypted_master_key[-16:])

first 16 bytes are ignored?
2nd 16 bytes are the "iv" (initialisation vector)
last 16 bytes are the "ct" (cipher text)


The hex that the walletinfo.py script outputs is:
Code:
s = iv + ct + binascii.hexlify(salt) + iterations

return s
So it is actually iv (16 bytes) + cipher text (16 bytes) + salt (8 bytes) + iteration (8 bytes, it gets padded out from 4 bytes)

I assume this is because the OpenCL portion of that project is expecting the data in this format (to test passphrases) with.
member
Activity: 180
Merit: 38
It looks for a specific header.
full member
Activity: 217
Merit: 109
I don't think you will be able to use that site, as there are some other background things happening that I don't think that website supports, but assuming you can decrypt the master key, then it should theoretically be possible to then decrypt individual private keys.


As for decrypting the master key, I found this project which claims to be able to extract the encrypted master key, salt, IV etc from a wallet.dat: https://github.com/brichard19/core-decrypt

There is also an OpenCL based project included (and a precompiled .exe Huh) that claims to be able to test the master key decryption using a password dictionary. The idea being that you can identify what the wallet passphrase is... it doesn't decrypt individual keys, and it doesn't show the decrypted master key etc. It just seems to test the "encrypted master key" decryption using passwords you pass in.


If this information regarding wallet.dat encryption is still valid: https://en.bitcoin.it/wiki/Wallet_encryption

Then it looks like you would need to start playing with your wallet passphrase, SHA512 and OpenSSL "EVP_BytesToKey" functionality (along with the extracted IV, Salt, iteration count etc as extracted by the python script) to derive the "key" needed to decrypt the "encrypted master key"...

and then once you have the "unencrypted master key", you should be able to decrypt the individual private keys.
The passphrase is no problem as I already know it. I wonder if the salt and iteration are viewable within the wallet file? How do these various scripts find them?
HCP
legendary
Activity: 2086
Merit: 4363
I don't think you will be able to use that site, as there are some other background things happening that I don't think that website supports, but assuming you can decrypt the master key, then it should theoretically be possible to then decrypt individual private keys.


As for decrypting the master key, I found this project which claims to be able to extract the encrypted master key, salt, IV etc from a wallet.dat: https://github.com/brichard19/core-decrypt

There is also an OpenCL based project included (and a precompiled .exe Huh) that claims to be able to test the master key decryption using a password dictionary. The idea being that you can identify what the wallet passphrase is... it doesn't decrypt individual keys, and it doesn't show the decrypted master key etc. It just seems to test the "encrypted master key" decryption using passwords you pass in.


If this information regarding wallet.dat encryption is still valid: https://en.bitcoin.it/wiki/Wallet_encryption

Then it looks like you would need to start playing with your wallet passphrase, SHA512 and OpenSSL "EVP_BytesToKey" functionality (along with the extracted IV, Salt, iteration count etc as extracted by the python script) to derive the "key" needed to decrypt the "encrypted master key"...

and then once you have the "unencrypted master key", you should be able to decrypt the individual private keys.
full member
Activity: 217
Merit: 109
Would an offline version of this work if you had the mkey ckey and passphrase? https://www.devglan.com/online-tools/aes-encryption-decryption
member
Activity: 180
Merit: 38
I've searched the 0201010420 trailing bytes and checked hundreds of private keys manually but with no success as yet. I could see they were bitcoin core keys with the data and text surrounding them. I suspect that the private key I need is encrypted and there are hundreds of those as well, plus mkeys. I noticed that each ckey marker 000104636b65 79 is followed at a point later by the trailing byte of 30 as discovered and shown in HCP's example. I'm thinking that maybe the bytes following 30 is the encrypted key and the public key is between 79 and 30 maybe?

If you can grab the entire block of encrypted data from start to end then you can decrypt it using standard methods in which you start with your IV (Initialization Vector) so from the start and not like a partial/damaged or orphaned block in which you need to use the chain code from the previous block.
From what you wrote it seems that the data is still intact so there should be no problem to find it.
full member
Activity: 217
Merit: 109
I've searched the 0201010420 trailing bytes and checked hundreds of private keys manually but with no success as yet. I could see they were bitcoin core keys with the data and text surrounding them. I suspect that the private key I need is encrypted and there are hundreds of those as well, plus mkeys. I noticed that each ckey marker 000104636b65 79 is followed at a point later by the trailing byte of 30 as discovered and shown in HCP's example. I'm thinking that maybe the bytes following 30 is the encrypted key and the public key is between 79 and 30 maybe?
member
Activity: 180
Merit: 38
What would be the minimum data required to decrypt a private key? Would it be ckey! and mkey, what about salt, is that part of the mkey? All depends on having the correct passphrase of course. My corrupt wallets have been either fragmented with groups of ckeys that have been separated from the mkey, or other data in the wallet has been corrupted causing the wallet to be unusable with normal methods. I'm pretty sure that the hex values of the mkey and ckeys can be found quite easily by using Winhex or similar. If possible, it would be fantastic to have a tool for manually inputting the keys and passphrase that doesn't get tied up with other corrupt data in the wallet.dat.

1. The target address. ( maybe not see below)
2. The passphrase (and encryption method)
3. The exact 32 (consecutive) bytes of the private key somewhere in the file.

For un-encrypted keys:
The principle is very simple and assumes that the key is still somewhere in the (corrupted / partly overwritten etc.) file.
You slide over all of the remaining file data in 32 byte sized blocks and turn every block into a address (of the same target address method).
Then you compare the generated address to the target address.
If there is no match you move or slide the block to the next nibble or byte.
Then when your byte scanner moves above the correct block of bytes, that holds the correct byte sequence, the resulting address will match the target address.
You can then print out/save the found data/key and exit.
This will work without the presence of any btree structure and without (looking for) the original database index keys because it only looks for the exact 32 bytes of key itself and not the combined database index entry plus relevant data.
In this respect this method will outperform recovery tools that only look for the standard database index key entries and their following variable field.
So it's as close to a RAW method as it can possibly get, simply because one nibble or byte more, or one less and you will completely miss your target.
It will work most of the time but not in case of missing/corrupt bytes in the key itself and for fragmented keys because then it will obviously produce a mismatch in target address.
In that case your chances on a success are extremely limited and will depend on how hard you decide to push it further with the scrambled data that is left.
You could start and try to rotate individual bytes but it could take forever, this is not something I would advice in combination with this method.
In case you do not know the target address you will have to verify the actual balance of every address that rolls out of those scanned blocks on the blockchain.
So you could even do it with less.



For encrypted keys it will get a bit more difficult because your goal then is the decryption of partial encrypted data and then you (usually) also need the preceding 16 bytes (CBC Cypher Block Chain) as part of the decryption method to enable the decryption of the next block of data depending on the decryption method you use, there are several but it get's a bit technical here it is best imagined that you no longer search for the exact key but for a block of data that 'makes sense' by this i mean when your output will be mostly garbage and non base16 characters then you know that you are off target, and when a block arrives with a perfect string of only base16 hexadecimal bytes then you can be absolutely sure that you have something of real value.
I always compare it to turning a knob of those old analog radio's it's mostly noise until a crystal clear channel pops up.
full member
Activity: 217
Merit: 109
What would be the minimum data required to decrypt a private key? Would it be ckey! and mkey, what about salt, is that part of the mkey? All depends on having the correct passphrase of course. My corrupt wallets have been either fragmented with groups of ckeys that have been separated from the mkey, or other data in the wallet has been corrupted causing the wallet to be unusable with normal methods. I'm pretty sure that the hex values of the mkey and ckeys can be found quite easily by using Winhex or similar. If possible, it would be fantastic to have a tool for manually inputting the keys and passphrase that doesn't get tied up with other corrupt data in the wallet.dat.
member
Activity: 180
Merit: 38
I've tried it for a bit & looks like it works. However since it dump all data and looks like all dumped data use HEX format, there are few problem
1. We don't know the data is private key, public key or other data (maybe it's possible by check HEX length).
2. We don't know whether is encrypted or not (since the tools worked on both encrypted & non-encrypted wallet).
3. Some regex needed to select only needed data (Assuming the characteristic of data you're looking for).
That's just a basic syntax and using designated database tools/utility since the topic starter wrote he wanted something else then Python.

True, but OP mentioned he had hard time time getting pywallet works, so your solution isn't what OP looking for (but useful for more advance user who read this thread).
[/quote]

You don't know that.
It's only two lines of code.
Maybe it was exactly what he was looking for.
member
Activity: 180
Merit: 38
The easiest way to find the desired key is to extract all data and then use the address as identifier to search the file and then find the key that relates to that particular address.

I read somewhere on Github that the wallet.dat file format sometimes does not write keys and values contiguously but rather in arbitrary positions, so we may end up with a bunch of rows like HCP's output but with keys and values split along multiple rows.

For example, the output lists 84 entries. I highly doubt Bitcoin Core's code defines that many keys.

It won't because a generic database dumper is not designed to specifically read AES keys and decrypt them with a secret stored somewhere else in the file.

You can deal with that if you know what you are doing as is with most things.
These are regular database files they have keys as identifiers which relate to the target data, that is how they work.
Your doubts and beliefs are really irrelevant because these things work along exact protocols that are very well defined.
If you want you can always extend functionality, i do this all the time.
There can be many keys in a database file.



I made a wallet parser specifically for corrupt wallet files that works on the byte level and it produces ~ 200.000 keys from just the one wallet.
It does so because it is told to ignore the key index since these could have become corrupted and so instead of using those, it assumes the possible data locations.
This results in huge files with countless possibilities.
It leaves no byte unturned and if the key is still in there, then it's going to find it.
That is working on a much deeper level than what most programs and scripts do and it takes more effort and time but the results will be crystal clear.
If there is any doubt you can just pull up the oracle documentation and read the specifications because these things are well defined and used in many applications so not just for crypto currencies.
jr. member
Activity: 190
Merit: 1
I can help you, i have some command lines that can get the info. How much do you have in it? Its complicated but i can help you along the way or you can reach me by email:[email protected]
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
The easiest way to find the desired key is to extract all data and then use the address as identifier to search the file and then find the key that relates to that particular address.

I read somewhere on Github that the wallet.dat file format sometimes does not write keys and values contiguously but rather in arbitrary positions, so we may end up with a bunch of rows like HCP's output but with keys and values split along multiple rows.

For example, the output lists 84 entries. I highly doubt Bitcoin Core's code defines that many keys.

The real issue is extracting the encrypted data... I'm not sure if the db_dump "-P" parameter is actually able to decrypt the encrypted keys or if it is meant for some other purpose. I can't get it to produce the unencrypted keys in the db_dump output. Undecided

I guess no, Bitcoin Core wallet only encrypt some information (e.g. private key and master private key), while others (e.g. public key and address) isn't encrypted.

It won't because a generic database dumper is not designed to specifically read AES keys and decrypt them with a secret stored somewhere else in the file.
member
Activity: 180
Merit: 38
Code:
sudo apt-get install db-util

Code:
db_dump -d a wallet.dat > walletdump.txt

I've tried it for a bit & looks like it works. However since it dump all data and looks like all dumped data use HEX format, there are few problem
1. We don't know the data is private key, public key or other data (maybe it's possible by check HEX length).
2. We don't know whether is encrypted or not (since the tools worked on both encrypted & non-encrypted wallet).
3. Some regex needed to select only needed data (Assuming the characteristic of data you're looking for).

That's just a basic syntax and using designated database tools/utility since the topic starter wrote he wanted something else then Python.
Of course you can extend it into a script that will plot keys and addresses and the relevant info you desire since its a btree and you can use bytekeys.
Oracle can deal with encryption but from what i can see it is likely that if there is any encrypted data then it will be encrypted and decrypted by the bitcoin application itself, in such case the database file is just a structure that hold keys it does not know anything about encryption it just stores the data so in those cases a encryption switch parameter can not be used to work with encrypted data in the tree.
The easiest way to find the desired key is to extract all data and then use the address as identifier to search the file and then find the key that relates to that particular address.
HCP
legendary
Activity: 2086
Merit: 4363
1. We don't know the data is private key, public key or other data (maybe it's possible by check HEX length).
Should be easy enough given that the Database Schema is public knowledge...

Even some basic "trial and error" type testing using PyWallet output or other known data (private keys/public keys etc) should mean it would be relatively simple (if somewhat time consuming) to be able to determine which outputs in the db_dump are the private keys etc.

for instance:
Code:
page 4: btree leaf: LSN [0][1]: level 1
        prev:    0 next:   84 entries:   90 offset: 2432
        [000] 8148 len:  39 data: 04636b6579210201df7814f1bfdc65694230553d9fea229e34fc096b27611881e5924e045e955e
        [001] 8064 len:  81 data: 305048baa5553a6e650272560b11709abe027d96d01a6cdb67646632ebb3d39bafdeb5af63e8386e899c5f03194afd925e5a0dea80180867296864761c6d4a583d21dc7b6ac7a7cea38c2eea0825a25aef
        [002] 8020 len:  39 data: 04636b6579210201e1dadb18af47c2f457aeb9892e9bc9986101d2a5a32cb85fce217b191472f9
        [003] 7936 len:  81 data: 300f05de99d0a5bcaa86f15d9fc7d809b45a61f6826e559d0041f48f5fa4568b495cb973d7d8238a85e5afabce56e5fcfb0d7e0f66e7379f04fc181544273bfdde701587c745ce0d552d25546bcf01c69c

From PyWallet:
Code:
    "keys": [
        {
            "addr": "1NU7VwXy3Ugb1NPFYy9Y76Q6DvzMtrihuj",
            "compressed": true,
            "encrypted_privkey": "5048baa5553a6e650272560b11709abe027d96d01a6cdb67646632ebb3d39bafdeb5af63e8386e899c5f03194afd925e",
            "pubkey": "0201df7814f1bfdc65694230553d9fea229e34fc096b27611881e5924e045e955e",
            "reserve": 1
        },
        {
            "addr": "18NvdAH4yGytm6C9vpPAvrHHXLdKwaERFK",
            "compressed": true,
            "encrypted_privkey": "0f05de99d0a5bcaa86f15d9fc7d809b45a61f6826e559d0041f48f5fa4568b495cb973d7d8238a85e5afabce56e5fcfb",
            "pubkey": "0201e1dadb18af47c2f457aeb9892e9bc9986101d2a5a32cb85fce217b191472f9",
            "reserve": 1
        },

We can see that the pubkeys are in the "len 39" data with a prefix of "04636b657921":
Quote from: pywallet
"pubkey": "0201e1dadb18af47c2f457aeb9892e9bc9986101d2a5a32cb85fce217b191472f9"
Quote from: db_dump
[000] 8148 len:  39 data: 04636b6579210201df7814f1bfdc65694230553d9fea229e34fc096b27611881e5924e045e955e


We can also see that the encrypted privkey data is in the "len81" data with a prefix of "30" and a unique trailing sequence of bytes:
Quote from: pywallet
"encrypted_privkey": "5048baa5553a6e650272560b11709abe027d96d01a6cdb67646632ebb3d39bafdeb5af63e8386e8 99c5f03194afd925e",
Quote from: db_dump
[001] 8064 len:  81 data: 305048baa5553a6e650272560b11709abe027d96d01a6cdb67646632ebb3d39bafdeb5af63e8386e8 99c5f03194afd925e5a0dea80180867296864761c6d4a583d21dc7b6ac7a7cea38c2eea0825a25aef


The real issue is extracting the encrypted data... I'm not sure if the db_dump "-P" parameter is actually able to decrypt the encrypted keys or if it is meant for some other purpose. I can't get it to produce the unencrypted keys in the db_dump output. Undecided
member
Activity: 180
Merit: 38
Code:
sudo apt-get install db-util

Code:
db_dump -d a wallet.dat > walletdump.txt
Pages:
Jump to: