Retaliating Against MetaMask Scammers With Python

Reconnaissance

Recently, I recieved this email:

fake metamask email

I have never used MetaMask. It was pretty obvious this was a scam. I decided to check it out anyway out of curiosity. It led me to this site which looked legit but had a major flaw: there is no domain and I’m accessing an insecure IP address.

fake metamask site

Still curious, I followed along and clicked on “Start verification process”. In the next page, there was a text field prompting me to input my seed phrase:

fake metamask input field for seed phrases

So I submitted 12 random words and used the browser dev tools to figure out where my seed phrase was being sent to.

fake metamask post-submission page

It looks like my fake seed phrase is being sent to /log.php as a query string through a GET request. I wanted to make these scammers pay somehow. I figured I could come up with a quick script to slam this endpoint with random seed phrases to waste their time.

To make my made up seed phrases look more legitimate, I needed to find out if the words in seed phrases have certain limits or if they come from a pool of words. I want the scammers to try every single phrase I submit only to find out that they don’t work.

After doing some reading from the official MetaMask site, I saw this:

metamask documentation

So there’s a specific list of words that seed phrases are generated from and I have a direct link to them. This was pefect. I could use this list of words in my script:

Scripting Time

I came up with a Python script that takes the list of words from the GitHub link, generates a 12 word seed phrase, and sends a GET request to the scammer URL along with the seed phrase as a query string:

import requests
import random

# List of words from https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
# (Trimmed due to long length)
SEED_PHRASE_WORDS = [
    "abandon",
    "ability",
    "able",
    "about",
    "above",
    .
    .
    .
]

# getting list of 12 random words
seed_phrase = random.sample(SEED_PHRASE_WORDS, 12)

# joining the list of 12 words into a single string
seed_phrase = " ".join(seed_phrase)

# query string for request
params = {"send_words": seed_phrase}

# sending GET request
response = requests.get("http://176.113.115.238/38sy2a0egs6zhxv/log.php", params=params)

# Output
print(f'Sent phrase: {seed_phrase}')
print(f'{response.status_code} {response.text}')

I ran this and it worked:

script output

The final step was to put this on a loop and leave it running for a very long time. I added a while loop. This is what the updated code looks like:

import requests
import random

# List of words from https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
# (Trimmed due to long length)
SEED_PHRASE_WORDS = [
    "abandon",
    "ability",
    "able",
    "about",
    "above",
    .
    .
    .
]

while True:
    # getting list of 12 random words
    seed_phrase = random.sample(SEED_PHRASE_WORDS, 12)

    # joining the list of 12 words into a single string
    seed_phrase = " ".join(seed_phrase)

    # query string for request
    params = {"send_words": seed_phrase}

    # sending GET request
    response = requests.get("http://176.113.115.238/38sy2a0egs6zhxv/log.php", params=params)

    # Output
    print(f'Sent phrase: {seed_phrase}')
    print(f'{response.status_code} {response.text}')

After that, I left my script running overnight:

script output with loop

The morning after, I noticed that my script output was stuck along with a new response message…

scammer response

I guess they caught on LOL. They blocked my IP address from sending requests, so I simply changed changed my IP address and carried on.


2022-07-25 update: I should have included headers in my requests to make them look as legitimate as possible. The requests Python package sends the version of the package itself in the User-Agent header, giving away the fact that Python is being used to send requests.

The following code shows an example of the User-Agent being sent:

import requests

r = requests.get('https://google.com')

print(r.request.headers)

# => {'User-Agent': 'python-requests/2.27.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

Using Little Snitch I was able to see that this IP address originates from Russia, which I thought was interesting.

Little Snitch connection to Russia

Final Thoughts

I hate scammers. I hope I made their lives difficult. If they’re smart, they’ll filter out seed phrases coming in from my IP address in their database. But considering that their fake MetaMask site doesn’t use a domain or SSL, they probably aren’t very bright. Even if my original IP address was blocked, I’m sure the seed phrases I provided polluted their database. I doubt they’re keeping track of IP addresses at the database level.