Manav.id
Crypto · 5 min read

Address poisoning works because humans check the ends

Address poisoning works because humans check the ends

Truncated display was a usability decision made when addresses were long and screens were small. It created a verification habit that costs nothing to defeat and is now the basis of an entire attack class.

Why does address poisoning work?

Because nobody reads forty hexadecimal characters. People check the first four and the last four, and generating an address that matches eight chosen characters costs minutes on commodity hardware. The attacker then places the lookalike in the victim's own transaction history, where it will later be copied.

Key takeaways
  • Matching a short prefix and suffix is computationally cheap, so the check most people perform provides little assurance.
  • Displaying a truncation is a lossy operation, and the security of the check depends entirely on what was elided.
  • Masking is acceptable when the full value is inside the signed statement; truncation with nothing behind it is not.

The arithmetic of a vanity match

Observe historyfrequent counterpartiespublicGrind a vanity match4 front, 4 backminutesSend dustappears in historyseedingVictim copies itfrom historyfunds gone
The attack never intercepts anything. It places a plausible wrong option where the user will reach for it.

Generating an address with chosen leading and trailing characters is a search. Each additional character multiplies the work by the size of the character set, and the base cost is low.

Characters matchedRelative search effort
4 total (2 front, 2 back)Trivial — seconds
8 total (4 front, 4 back)Cheap — commodity hardware, minutes to hours
12 total (6 front, 6 back)Meaningful but attainable
16 total (8 front, 8 back)Expensive
Full addressInfeasible

The second row is the common display truncation. It is within reach of anyone with a graphics card and patience, which is why this attack is executed at volume rather than against individual high-value targets.

How the poisoning happens

  1. The attacker observes a target's transaction history and identifies frequent counterparties.
  2. They generate an address matching the counterparty's visible prefix and suffix.
  3. They send a tiny or zero-value transaction from it to the target, so it appears in the history.
  4. The target later copies an address from their history — or from a wallet's suggestion list — and picks the wrong one.
  5. Funds go to the attacker.

Step four is the key. The attack does not need to intercept anything; it needs to place a plausible wrong option somewhere the user will reach for it.

Character-level tricks on labels

A related family attacks the human-readable name rather than the address.

Each of these makes two distinct strings look identical to a person. Normalising and comparing at the codepoint level catches them; visual comparison does not.

Masking versus truncation

There is an important distinction between hiding characters for readability and discarding them.

# Truncation — the elided characters are gone from the signed data
  "to": "0x9c1f...a83e"
  → the signature covers only what is shown
  → a different full address could produce the same display

# Masking — the full value is present, display is abbreviated
  "to": "0x9c1f8a3e2b7d4056891fa2c73e1b9d40a2f8c31e",
  "to_display": "0x9c1f…8c31e",
  → the signature covers the full address
  → a verifier compares the full value, not the abbreviation

The rule that follows: abbreviate for display, sign the whole thing, and verify against the whole thing. Never let the abbreviated form be the authoritative value.

Interface changes that help

ChangeEffect
Show the full address, paginated or wrapped, for first-time recipientsRemoves the truncated-check habit where it matters most
Label known counterparties from a user-curated list, not from transaction historyHistory is attacker-writable; a curated list is not
Flag first-time recipients explicitlyThe attack depends on a new address looking familiar
Normalise and compare labels at codepoint levelCatches zero-width and homograph tricks
Warn when a new address closely resembles a known oneThis resemblance is the attack's signature

The second row is the highest-value change and the least implemented. Populating an address book from received transactions means an attacker can write to your address book.

For institutional flows

Where counterparties are known, the problem is more tractable and the answer is stricter: an allow-list of full addresses, and a separate signed process for adding to it.

Adding a counterparty becomes the gated event rather than each payment. That is the right place for the control, because the address change is the moment of exposure and payments to an established address are routine.

The arithmetic of a vanity match

Matching chosen characters is a search, and the base cost is low. Each additional character multiplies the work by the size of the character set, which is what makes the standard display truncation exactly the wrong length.

Search effort by characters matched
Characters matchedEffort
4 total (2 front, 2 back)Trivial — seconds
8 total (4 front, 4 back)Cheap — a graphics card and some patience
12 totalMeaningful but attainable
16 totalExpensive
Full addressInfeasible

A related family attacks the label rather than the address: zero-width characters inserted into a name, characters from other scripts that render identically, trailing whitespace, directional override characters. Each makes two distinct strings look the same to a person. Normalising and comparing at the codepoint level catches them; looking catches none of them.

Objections and honest limits

“Our users are told to check the full address.” They are told, and they check the ends, because the interface shows the ends. Behaviour follows the display. Change the display for first-time recipients rather than the guidance.

“The address book solves this.” Only if it is user-curated. An address book populated from transaction history means an attacker can write to your address book by sending you dust.

Interface changes that actually help

  1. Curate the address book manually. Never populate it from received transactions. This is the single highest-value change and the least implemented.
  2. Show the full address for first-time recipients. Paginated or wrapped. Remove the truncated-check habit where it matters.
  3. Flag first-time recipients explicitly. The attack depends on a new address looking familiar.
  4. Normalise and compare labels at codepoint level. Catches zero-width and homograph tricks that visual comparison cannot.
  5. Warn when a new address resembles a known one. That resemblance is the attack's signature.

Terms used here

Address poisoning
Seeding a lookalike address into a victim's transaction history so it is later copied in place of the real counterparty's.
Vanity address
An address generated by brute-force search to contain chosen characters, typically at the start and end.
Homoglyph
A character from one script that renders identically to one from another, so two distinct strings look the same.

Frequently asked questions

Why is matching a prefix and suffix cheap? It is a search whose cost grows with the number of characters matched. Eight total characters is minutes to hours on commodity hardware.

How does the wrong address get into the user's history? The attacker sends a tiny or zero-value transaction from the lookalike address, so it appears in the history the user later copies from.

What is the difference between masking and truncation? Masking keeps the full value in the signed data and abbreviates the display. Truncation signs only what is shown, so a different address could produce the same display.

What is the highest-value interface change? Populating the address book from a user-curated list rather than transaction history. Otherwise an attacker can write to your address book.

How cheap is a matching address? Eight total characters — the usual display truncation — is minutes to hours on a single graphics card. That is why the attack runs at volume rather than against individuals.

How does the wrong address get into my history? The attacker sends a tiny or zero-value transaction from the lookalike address. It then appears in the history you later copy from.

What should institutional flows do instead? Gate the addition of a counterparty rather than each payment. The address change is the moment of exposure; payments to an established address are routine.

Where this fits in Manav

Manav's rule is that masking is permitted only when the full value is inside the signed statement. Abbreviate for display, sign the whole thing, verify against the whole thing — never let the abbreviation be the authoritative value.

See statement rules →

Sources and further reading