Python 3 and a terminal — nothing else. macOS and Linux have it
already; on Windows install it from python.org with “Add python.exe to
PATH” ticked, then check with python3 --version. Every module used
below ships with Python, so there is nothing to install.
Nothing here visits a website. The fake domains are typed into your own files and examined offline — you will never be asked to open a suspicious link to see what happens, which is advice that gets people robbed. What you are building is the habit of answering “which site is this actually?” with a check instead of a glance.
Why Crypto Phishing is Different
Traditional phishing steals passwords, which can often be reset. Crypto phishing steals private keys or seed phrases, which cannot be reset. One successful phishing attack can permanently drain your entire wallet in seconds. There is no customer support to call, no chargeback to file, and no way to reverse the transaction.
Fake Wallet Websites
Scammers create pixel-perfect copies of popular wallet interfaces:
- Fake versions of MetaMask, Phantom, Ledger Live, and other popular wallet sites
- URLs use subtle tricks:
metamask.iovsmetamask-io.comvsrnetamask.io(rn looks like m) - These sites ask you to "import your wallet" by entering your seed phrase
- The moment you enter your seed phrase, all funds are automatically swept
Seed phrases are only entered directly into wallet software or hardware devices during recovery. Any website asking for your seed phrase is a phishing site, without exception.
Malicious Browser Extensions
- Fake wallet extensions published to Chrome Web Store or Firefox Add-ons with names similar to real wallets
- Some request excessive permissions that allow them to read all website data
- They may overlay fake transaction confirmations or modify displayed addresses
- Always install wallet extensions from the official project website link, never from searching the extension store directly
Clipboard Hijacking
Clipboard hijacking malware monitors your clipboard for cryptocurrency addresses. When you copy an address to send funds:
- The malware detects the address format (Bitcoin, Ethereum, etc.)
- It silently replaces the copied address with the attacker's address
- When you paste, you paste the attacker's address without realizing it
- You send funds directly to the attacker thinking you sent them to the intended recipient
Check the first and last 6-8 characters of the address after pasting. This takes five seconds and can save you from losing everything.
Address Poisoning
A newer and particularly insidious attack:
- The attacker generates a wallet address that matches the first and last few characters of an address you regularly send to
- They send a tiny transaction (dust) from this lookalike address to your wallet
- This transaction appears in your history, looking like the address you normally use
- Next time you need to send funds, you might copy the attacker's lookalike address from your transaction history instead of the real one
Defense: never copy addresses from transaction history. Always use your address book or get the address directly from the intended recipient.
The same reasoning applies to the tokens themselves. A counterfeit can copy a familiar name, ticker and icon exactly, so a token's identity has to be confirmed from its contract address rather than from the label a wallet displays — see How to Verify a Token Contract Address.
Fake Support Channels
- Scammers monitor social media for users posting about wallet problems
- They DM the user pretending to be official support, often within minutes
- They direct the user to a "support tool" or "wallet repair site" that harvests seed phrases
- Real support teams will never DM you first and will never ask for your seed phrase
Email Phishing for Exchange Accounts
- Fake "security alert" emails from exchanges urging you to log in immediately
- Links lead to convincing replicas of the exchange login page
- After capturing your credentials, attackers log in and initiate withdrawals
- Some phishing kits also intercept 2FA codes in real time (adversary-in-the-middle attacks)
How to Protect Yourself
- Bookmark official sites and always navigate from bookmarks, never from search results or links
- Verify URLs character by character before entering any credentials
- Use a hardware wallet that shows transaction details on its own screen
- Enable anti-phishing codes on exchanges that support them
- Verify the full address after pasting, before every transaction
- Never enter your seed phrase into any website
- Use hardware-based 2FA (security keys) when possible, as they are phishing-resistant
Now Catch a Phishing Domain Yourself, in Five Steps
Crypto phishing does not beat your judgement — it beats your eyes. In the next fifteen minutes you will hold two domain names that are visually identical and prove they are different sites, watch what your browser really requests when you click one, build a two-line detector that catches the whole trick, learn to read a web address in the only direction that tells the truth, and finally put a number on the “approve” button that empties wallets. Every line of output below came from running these files.
Go: open a terminal in a folder you can write to, for instance
cd ~/Desktop (Windows: cd %USERPROFILE%\Desktop).
Do: save this as lookalike.py and run
python3 lookalike.py. Copy it exactly — the fourth character of the second
name is a Cyrillic letter, and copying by hand will destroy the whole point.
real = "binance.com"
fake = "binаnce.com" # the 'a' here is Cyrillic U+0430
print("they look the same :", real, fake)
print("they are the same :", real == fake)
print()
for label, name in (("real", real), ("fake", fake)):
print(label, "->", " ".join(f"U+{ord(c):04X}" for c in name[:8]))
You should see: two names your eyes cannot separate, and one position where the computer can:
they look the same : binance.com binаnce.com
they are the same : False
real -> U+0062 U+0069 U+006E U+0061 U+006E U+0063 U+0065 U+002E
fake -> U+0062 U+0069 U+006E U+0430 U+006E U+0063 U+0065 U+002E
Position four: U+0061 is the Latin letter a, U+0430 is
the Cyrillic letter а. They are drawn identically in almost every font on
earth and are completely different characters, so they are completely different domains,
owned by different people. No amount of careful looking will separate them.
If not: if the second line prints True, your editor
“helpfully” normalised the Cyrillic character back to Latin when you pasted
— use a plain editor such as Notepad, TextEdit in plain-text mode, or
nano. If you see SyntaxError: invalid character, a smart-quote
replaced a straight quote; retype the quotation marks.
Go: same folder.
Do: save this as punycode.py and run it. Domain names can
only travel as ASCII, so anything else is converted first — and the conversion is
visible.
fake = "binаnce.com"
print("what your eyes read :", fake)
print("what the browser fetches:", fake.encode("idna").decode())
You should see: the disguise falling off:
what your eyes read : binаnce.com
what the browser fetches: xn--binnce-5nf.com
That xn-- prefix marks a name containing non-ASCII characters. Most browsers
show it in the address bar for exactly this reason, so a wallet or exchange address
that suddenly reads xn-- is not a glitch — it is the warning.
Some browsers only show it for certain scripts, which is why the next step exists.
If not: UnicodeError: label empty or too long means the
string lost its Cyrillic character in copying, leaving something IDNA cannot encode. If it
prints binance.com unchanged, the same thing happened — a pure-ASCII name
converts to itself.
Go: same folder. Two lines is all the defence needs.
Do: save this as detect.py and run it.
def looks_safe(domain):
bad = [c for c in domain if ord(c) > 127]
return "SAFE (plain ASCII)" if not bad else \
"SUSPICIOUS: " + ", ".join(f"{c!r} = U+{ord(c):04X}" for c in bad)
for d in ["binance.com", "binаnce.com", "ledger.com", "lеdger.com"]:
print(f"{d:16s} {looks_safe(d)}")
You should see: the two impostors named, with the exact character that gives them away:
binance.com SAFE (plain ASCII)
binаnce.com SUSPICIOUS: 'а' = U+0430
ledger.com SAFE (plain ASCII)
lеdger.com SUSPICIOUS: 'е' = U+0435
Keep this file. When a link arrives by email, message or a search result, paste the domain into the list and run it. It takes three seconds and catches an attack class that careful reading cannot.
If not: if every line says SAFE, the Cyrillic characters were lost in copying — go back to step 1 and copy from the page again. Legitimate international domains do contain non-ASCII characters, so treat SUSPICIOUS as “stop and verify by another route”, not as proof of fraud.
Go: same folder. This step needs no Unicode trickery at all — these domains are honest ASCII, and still lie.
Do: save this as whereami.py and run it.
from urllib.parse import urlparse
urls = [
"https://app.uniswap.org/swap",
"https://app.uniswap.org@wallet-drainer.xyz/connect",
"https://metamask.io.security-check.app/verify",
"https://trezor.io/start",
]
for u in urls:
host = urlparse(u).hostname
print(f"{host:32s} <- {u}")
You should see: two of the four going somewhere other than where they appear to:
app.uniswap.org <- https://app.uniswap.org/swap
wallet-drainer.xyz <- https://app.uniswap.org@wallet-drainer.xyz/connect
metamask.io.security-check.app <- https://metamask.io.security-check.app/verify
trezor.io <- https://trezor.io/start
Two separate tricks. Everything before an @ in a web address is ignored as a
username, so the second link goes to wallet-drainer.xyz while displaying a name
you trust. And in the third, metamask.io is merely a subdomain of
security-check.app — anyone can create it in thirty seconds.
Ownership is decided by the last two labels before the first slash, read
right to left. Everything to their left is chosen by whoever owns them.
If not: if the second line prints app.uniswap.org, the
@ was dropped in copying. If hostname returns None,
the URL is missing its https:// prefix.
Go: same folder. The last step of a crypto phishing attack is rarely “send us your coins” — it is a token approval that looks routine.
Do: save this as approval.py and run it.
MAX = 2**256 - 1
ONE_TOKEN = 10**18
print("amount the popup is asking you to approve:")
print(" raw :", MAX)
print(" tokens:", f"{MAX // ONE_TOKEN:,}")
print()
print("what a 50-token swap actually needs:")
print(" raw :", 50 * ONE_TOKEN)
print(" tokens: 50")
You should see: the two numbers side by side:
amount the popup is asking you to approve:
raw : 115792089237316195423570985008687907853269984665640564039457584007913129639935
tokens: 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457
what a 50-token swap actually needs:
raw : 50000000000000000000
tokens: 50
That first number is the largest value the token standard can hold, and wallets often render it as “Unlimited” or simply as a wall of digits nobody reads. Approving it hands a contract permission to move that token out of your wallet at any point in the future, with no further prompt. Before approving anything, look for the amount and check it is roughly the size of the trade you are making. A swap for 50 tokens that requests an unlimited allowance is asking for something it does not need.
If not: ValueError: Invalid format specifier means the
:, inside the f-string was mistyped. If the token line looks rounded or ends in
zeros, you used / instead of // — ordinary division loses
precision at this size, which is itself a good reason to distrust a displayed amount.
Without scrolling up: a search result shows https://ledger.com.wallet-verify.net/recover
and the page looks perfect. Which site are you on, why did the padlock and the “https”
not help, and what is the one thing that page will eventually ask for? Answer: you are on
wallet-verify.net — ownership is the last two labels, and
“ledger.com” is just a subdomain the attacker created. The padlock only means the
connection to that attacker is encrypted; anybody can obtain a certificate for a domain they
own, in minutes, for free. What it will ask for is your recovery phrase, usually framed as
“validate”, “sync” or “restore your wallet” — and no
genuine wallet, exchange or support agent ever needs it.
Now do it without the page: take the last three links that reached you by
email or message — any links, not only crypto ones — and run each through both
detect.py and whereami.py. Write down the registrable domain for
each one before you look at what you expected. Doing this on ordinary mail is how the habit
becomes automatic before the day it matters.
Summary
- Crypto phishing is more dangerous than traditional phishing because losses are irreversible
- Fake wallet sites, malicious extensions, and fake support are the most common vectors
- Clipboard hijacking and address poisoning silently replace destination addresses
- Always verify full addresses, bookmark official sites, and never enter seed phrases online
Next, learn about rug pulls and how to spot fraudulent crypto projects before they take your money.