function approve(address _spender, uint256 _value) public returns (bool success)
. This allows a third-party to send tokens from your account on your behalf. You’ll recognize this pattern if you’ve ever done a swap through an AMM (like Uniswap) as you’ll have to approve the AMM to spend your tokens before you swap.
Bad actors have learned how to exploit this function, as it’s harder for unsuspecting users to avoid when they’re only expecting scams that outright ask for their private keys (like they usually do). Exploiting token approvals is a clever approach because users generally think: “If they don’t have my key then they can’t sign a transaction, so they cannot steal my assets.” Whilst generally accurate, this is not true when we talk about ERC20 tokens (and other token standards).
Right now, at least on my radar, is a scam campaign targeting Chainlink token holders (Chainlink has a market capitalization at $11B with ~450,000 holders). The funnel for this scam campaign is a mailing list — check your inbox for [email protected] and delete and unsubscribe (be vigilant, as they may resell this mailing list or use it again).
The malicious campaign details a fake upgrade to the token which promises 68% less gas, supports meta-transactions, and allows you to participate in future votes.
The promise of less gas is supposed to strike FOMO into the hearts of users so they “upgrade” as soon as possible without thinking — this is taking advantage of the current high gas prices on the Ethereum network and peoples’ desires to spend less on TX fees.
The scam email campaign outlines details on how to upgrade, and the bad actors are publishing a verified contract on-chain to make it look more legitimate. However, if a user reads the contract, they would understand how the token approval call is abused to steal their tokens.
The guide is generic, lengthy, and includes screenshots on how to execute this upgrade using popular UIs, such as MyCrypto and MyEtherWallet. The scam then takes its final form when users are prompted to set an approve() for their contract address at 1,000,000,000 tokens for each user. This means that the bad actors would then have permission to withdraw up to 1,000,000,000 Chainlink tokens (or the tokens from whichever protocol is being targeted) from the account approval was given on.
transferFrom()
on the Chainlink token smart contract, allowing them to “drain” users’ account balances.
At time of writing, 4 unique addresses have followed the steps to sign the approve() transaction:
- 0x2a5c6607ef091f96ee1fca635db56eff18ad1fc8 (tx)
- 0x7923338eb2c6b8b43407f8fd3fd75826c1c64c8c (tx)
- 0xc744a98c2e745c5da9338f9a82fdedbf6778a96d (tx)
- 0xf7db1796ee8603f3a5de43793581e038027fb9fb (tx)
transferFrom()
on their contract address and move tokens from the unsuspecting users’ accounts into an address that they control.
Below is the verified smart contract code on Ethereum mainnet. It’s also worth noting that this contract LessGasProxy is token agnostic, so this campaign is probably targeting other token communities.
pragma solidity 0.6.12;
interface IERC20Token {
function allowance(address _owner, address _spender) external view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
}
contract LessGasProxy {
address public owner;
constructor() public {
owner = msg.sender;
}
function transferFrom(IERC20Token _token, address _sender, address _receiver) external returns (bool) {
require(msg.sender == owner, "access denied");
uint256 amount = _token.allowance(_sender, address(this));
return _token.transferFrom(_sender, _receiver, amount);
}
function transferGas(IERC20Token _token, address _sender, address _receiver, uint256 _amount) external returns (bool) {
require(msg.sender == owner, "access denied");
return _token.transferFrom(_sender, _receiver, _amount);
}
}
The bad actor’s EOA (externally owned account)(0x71899b741e2316b756ad98cdf425d84f996017a0) that can drain users’ tokens has been funded only once from another account (0xe7f7d950e32b98a619439abd6f2e6463f3a2d908).
So far, the bad actors have moved 266 LINK tokens (valued at $7,700). However, the address they are sending the tokens to has a current balance of 1,111.48653745 LINK tokens (valued at ~$32k) so there are likely multiple campaigns from the same bad actors.
If you or someone you know may have approved unnecessary amounts of tokens on any product, fix it now with https://revoke.cash.
If you aren’t sure if you have given unnecessary approvals or not, audit yourself anyway.- Make sure you trust who you are approving to spend your tokens, and understand the consequences of calling
approve()
on a token contract! - Audit who has spending control of your tokens and revoke this control if you do not trust/use the spender anymore (tools such as https://revoke.cash/).
- If you are one of the key holders to any of the four mentioned addresses that have already given spending permissions to the bad actors, as your priority task you will need to revoke spending access immediately by calling
approve()
with 0 tokens. - Ensure you are only consuming updates from official sources who maintain the token contract code — including adding your email to trusted mailing lists!