Recovering an Incomplete Monero Mnemonic Wallet

in #monero8 years ago (edited)

A few days ago, a fellow steemit member, @generalizethis, posted a challenge to recover some free Monero from a mnemonic wallet that he incorrectly copied.

From that post:

During Christmas in 2014, I made Monero wallets as stocking stuffers. Each wallet had a 100 monero in it ($25 at the time, now about $180). I'm not the most detailed orientated person, so needless to say, I forgot a word on one of the wallets. I never gave it away--figuring it would make a nice prize for someone with free time and a dictionary hack.

While I wouldn't say I had free time, this seemed like a lot of fun. I wound up finding the mnemonic with the prize intact. I had so much fun I figured I'd share.

TLDR

Github

The Challenge

The post included an incomplete mnemonic that omitted a single word, probably accidentally skipped over when being copied to paper:

mailed large soothe doctor onward odds zodiac avidly addicted fishing shyness avidly

We were also told that the mnemonic was usable on MyMonero.com
Given that MyMonero returns 13 word mnemonic wallets, we were looking for one word. One word worth 100XMR.

Finding the Seeds

At this point I wasn't really sure where to start. I figured that I would need to iterate through the entire dictionary of words before finding a potential match but even then I would need to decode the mnemonic into an address. Since I only had a rough idea of how that string of words would be converted into a wallet address, I went to MyMonero and opened up the developer console.

Searching through the source files, looking for any place to start, I struck gold when I happened upon a promising file named mnemonic.js that included a function called "mn_decode". The decode function required a list of words with a matching list of truncated words. Without the exact list of words, decoding this mnemonic would have been impossible. I chose to work in python so I translated the function over, omitting unnecessary features, and copied the necessary lists to file.

Now that I had the list of words, I iterated through each word, appending the new word to the end of the incomplete mnemonic. The result, a single valid mnemonic!

mailed large soothe doctor onward odds zodiac avidly addicted fishing shyness avidly fishing

Excited, I hurried to MyMonero lest the fund get extracted before I get there. What do I find:

Your Balance
0 XMR

Rediscovering Hope

Dismayed, I returned to the blog post to let everyone know the funds were gone, leaving a comment to this effect. While reading through the other comments, searching for the person who actually found the address, I noticed a comment asking about WHERE the missing word was. Realizing there was still a possibility, I rushed back to my terminal, added a for loop to my code, and found many more hits. There were now 1627 potential mnemonics to test.

"Finding" an API

I was not going to sit there, entering in 1627 login codes. So I searched for a way to automate this. MyMonero does not have an official api and I didn't have a synced blockchain. However, if you know the correct keys, you can make a request to

https://api.mymonero.com:8443/get_address_info

A valid request yields:

{
  "locked_funds": "0",
  "total_received": "0",
  "total_sent": "0",
  "scanned_height": XXXX,
  "scanned_block_height": XXX,
  "start_height": XXXXXX,
  "transaction_height": XXXXX,
  "blockchain_height": XXXX,
  "spent_outputs": [ ]
}

Any account with "total_received" greater than zero should have the reward.

Seeds to Address

Only problem here is the REST call above needed keys, not the mnemonic. I had a list of seeds (generated from the mnemonic), but not a list of addresses to use. Back to the source code.

In the cn_utils.js file, there is a function called "create_address" that returns the addresses we need given the seeds we have. I got a little lazy at this point and got lucky that the cn_utils object was globally accessible in the web console of MyMonero. I output my list of seeds to a JSON object, generated the addresses in the web console, output the results to another JSON object, and saved it to file.

Addresses to Reward

Using the list of addresses, a copy of a API call, and the requests library in python, I iterated through the 1627 addresses to see if any had value in them. Only one address had any value. There was 100XMR, added December in 2014. Here was the whole mnemonic:

mailed large soothe doctor onward odds zodiac avidly addicted fishing waking shyness avidly

Appreciation

I want to thank @generalizethis again for offering this challenge and reward. I had a lot of fun and learned a lot.

Also, thanks to MyMonero for the really nice website, well written code, and thousands of http requests.

The code is posted on Github

Sort:  

How fast can this process run? Could someone with a super computer find a way to check a percentage of valid addresses in a given time frame without a starting point of 12 words?

No super computer that exists today could ever find a specific mnemonic using the brute force method I used. I say a "specific" mnemonic because you can find plenty of addresses, they'll just be unused. If you click "Create An Account" on MyMonero, that is the computer, running an algorithm, and spitting out an address. Even I found over 1600 legitimate addresses. The idea here is that there are so many possible addresses that it's unlikely that you would randomly get the same one twice or that it would be completely unreasonable for anyone to search through even the smallest fraction.

Just to give you some math on this, a mnemonic here has 13 words chosen from a collection of 1627 words. That means that without understanding the details on how the algorithm works, there are 162713 , or 5.6 x 1054, possibilities. That's a 5 with 54 digits after it. The likelihood of guessing the mnemonic from a number that large is so small it's basically zero. Exponential growth is really crazy. For example, 1 million (106) seconds is 11.6 days and 1 billion (109) seconds is 31.7 years.

That being said, the algorithm has constraints in it so not EVERY combination of words is valid. Without even knowing the details, since the mnemonic is turned into a 32 digit hexadecimal seed that means there are 1632 possible seeds so it would be easier to just search seeds instead of mnemonic possibilities. So the real search space for the mnemonic is 3.4x1038. The chances of finding the right one on any given attempt are 1/(1632) == 2.9x10-39.

How many attempts would it take you say? Well, if you try half of the numbers, you have a 50% chance of finding the particular address you're looking for. So theoretically you might have to search them all. But in order to have a 50% chance of finding the right one you would have to run the algorithm 1.7x1038 times. Just to illustrate how hard much work this is, let's pretend that the algorithm to generate an address takes the same amount of time that it takes to run a single hash in the bitcoin mining operation. Right now, the entire bitcoin network runs on the order of 1018 hashes per second. Let's pretend we could shrink that into a box and give one of those boxes to every single human on earth (say 1010 people). That means earth could run 1028 hashes/second. Even in these conditions, it would still take us 540 years to have a 50% chance of finding the right one.

Relevant XKCD

What about if you weren't looking for any particular one and just wanted to find ANY address that had a non-zero value in it. In Monero we can't know for sure what any account has (go XMR!), so let's use bitcoin to compare. At the moment there are about 5.5 million addresses with more than $1 USD. If the same were true for Monero (and it's not), that means that finding any of them would be "profitable" and make you at least a dollar. At any given trial, the probability is the number of "hits" over the total number of possibilities. In this case, (5.5x106)/(1632) = 1.6 x 10-32. How long would the super computing earth I described above take to find any account? 1 hour and 42 minutes. If the current existing bitcoin network dedicated all its resources to this task right now it would take 1.98 million years to find an address.

That being said, there is another reason a super computer couldn't do it. Super computers are actually not the best way to go about this. Their CPU speed is incredibly fast, but because they are designed as general purpose computers, specialized hardware can outperform them in very limited domains. Big bitcoin mining operations use asic miners, not general computers. A collection of similarly priced bitcoin asic miners would mine bitcoins more effectively than a supercomputer.

As long as you keep your keys private, your coins are safe.

So does this mean if it happens again it is possible to recover a seed of mymonero that is missing a single word?

Yes. You can recover a single missing word rather quickly. Though after that, I don't think THIS particular method is effective. The main issue is that I didn't want to sync a full monero node. So instead I made network calls, which is REALLY slow. If instead of making network calls, I synced a full node and checked each account locally, then two or three missing words isn't unreasonable. After that you're really going to have to optimize and parallellize and wait a really long time. I estimate that there would be on the order of millions of valid 2 missing words, and billions with 3.

Hi, I have the same problem. I found 12 out of 13 of of my monero code. Could you help me retrieve the last one please? I'd really appreciate it!

if you add fish, you will see a new account that 0 XMR value

Congratulations @amustafa! You have received a personal award!

2 Years on Steemit
Click on the badge to view your Board of Honor.

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Congratulations @amustafa! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 3 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!