Real Examples of Address Poisoning
Address poisoning is when an attacker tries to insert a malicious address into your wallet’s “recently used addresses” list. They do this in the hope that when you next make a transaction, you will send it to the malicious address by accident. At the heart of this is a malicious address that looks a bit like a known good address, in the sense that it starts and ends with the same hex. This is the malicious address, the “poisoned address”.
The attack can involve sending you tokens from a malicious address. A more advanced variant of the attack involves spoofing the sending of tokens to a malicious address, such that they look like they originated from your address. We will look at both types, and explore a real world example.
Crafting Malicious Addresses
Let’s say you interact quite often with a known good address:
Address = 0x0040904745013aDE49C4589eC52b998CA6F09000
Key = 1bf2e5e2f7ac05c6e071ea1b6f2879e8f4c97473b7039dfcfa757b53e62f8a69
When checking it, you glance at the first 3 chars because you know it starts with “004” and you glance at the end which is “9 and some zeroes”.
The attacker wants to create a malicious address that looks a bit like the one above. There is a simple address generator available online https://vanity-eth.tk/. You can see how easy it is to generate an address with characters of your choice. In under 2 minutes we can produce this malicious address:
Address = 0x0044209858ca9a86FaA9E03F5520F8725449a900
Key = 6d0110d772bc2217946adea8dca0d58d8a6c76949424f11e019e79df30bc23f0
If you were in a hurry, you might glance at the above and say “yeah it starts with 004 and ends with 9 and some zeroes, we’re good”. The fact that the address was listed in your wallet’s “recently used” list further supports the idea that the address is good.
Now that we’ve got our malicious address, how does an attacker get that into our wallet’s “recently used” list?
Getting a Malicious Address into a Wallet’s “Recently Used” List
1. Recently Received From (aka Receiving Dust)
The receiving of ERC20 tokens cannot be prevented. It is of course very simple to send fake tokens, or to send miniscule amounts (“dust”) of real tokens to an address. So attackers can send something from a malicious address to your address, and these will appear in your wallet’s “recently used” or “transaction activity” list.
It depends on your wallet, but these will appear under the grouping of addresses labelled as “received from”. This is fair enough, but the attacker’s thinking goes that you are more likely to re-use their malicious address if you see it in your “recently sent to addresses” list. So the attacker’s goal is to get their malicious address into your “recently sent to” address list. But how could that be possible? Let’s find out…
2. Recently Sent To (aka Spoofing Sent Tokens)
For the attacker to pollute our “recently sent to addresses” list in our wallet, they exploit the fact that many wallets don’t care who sent a transaction, instead they listen for events. For example, imagine a legitimate ERC-20 token transfer of USDC from Alice to ourselves. When this happens, we see two things on-chain:
- A transaction occurs on-chain. This transaction could show
Transaction From Address: Alice
andTransaction To Address: USDC contract
. Or it could showTransaction From Address: Bob
andTransaction To Address: Some other contract
and that other contract makes the call to do the transfer of USDC from Alice to ourselves. The point is, it would be extremely complex to work out from just the raw transaction data (Transaction From Address
,Transaction To Address
and associated data) what movements of tokens are happening. - An ERC-20 transfer event is emitted by the USDC contract. No matter who initiated the transaction, or which contract first received the transaction, at some point the USDC contract will emit a
transfer
event withTransfer From Address: Alice
andTransfer To Address: ourselves
. If a transaction makes more than one transfer then more than onetransfer
event will be emitted.
From the point of view of the wallet, it wants to know what addresses it should put into the “recently sent to addresses” list. Given that 1.
is super-complex and 2.
is super-easy, it makes sense to rely on listening for events. This is what events are for, after all, and this is what the attacker exploits.
The attacker can deploy an evil contract that emits transfer
events with any Transfer From Address
and Transfer To Address
they wish. Their evil contact doesn’t actually move any tokens, or send anything at all. It only emits transfer
events that appear to be genuine ERC-20 events. The events emitted can have a From Address: Your Address
and a To Address: The Attacker's Malicious Address
. This To
address is the one that the attacker hopes you will pick by accident.
If we were to look more closely at this event, we’d see that the contract that emitted the event wasn’t USDC, or any known ERC-20. It was the attacker’s evil contract. There is no official central policing of ERC-20s, so wallets can’t easily tell if a transfer
event is genuine or not (although there are services that try to address this).
Real World Example
Qualifying as an Intended Target
These address poisoning attacks have a cost associated with them for the attacker. This cost may be small, depending on the chain, but they still have to pay gas and manage bulk emissions of events. So they don’t poison addresses willy-nilly, they target addresses that have recently made large, genuine, transfers.
Polygon Address Poisoning
This example is on Polygon, gas is very cheap which reduces cost for the attacker. The attacker has an “orchestration” contract that makes transfer()
calls out to many malicious tokens. One such malicious token is here.
Neither contract has source code associated with it which is a red flag for a token. We can however, take the bytecode and decompile it. An excellent service for this is Dedaub, which let’s us see the decompiled “pseudo-Solidity” like this:
function transfer(address _from, address _to, uint256 _wad) public payable {
emit Transfer(_from, _to, _wad);
}
As you can see, they’re not transferring anything, they just emit an event that says an address did a transfer when it did not. Wallets pick up those events and interpret them as a genuine transfer.
Effects on Other Services
Poisoning doesn’t just affect wallets. It affects any service that collects transfer information, for example services that calculate tax. Address poisoning isn’t a small problem, it is now possible to buy address poisoning toolkits(!), as detailed in this Chainalysis article.