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

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