Chainhunters
In EVM-gebaseerde blockchains gebruiken smart contracts events
om te communiceren met de buitenwereld.
Deze events worden gelogd als onderdeel van de transaction receipt en maken geen deel uit van de interne state
van het contract.
Stel je een openbaar logboek voor naast een kluis. Het logboek toont wie beweert items te hebben gedeponeerd of opgenomen. Maar tenzij je de daadwerkelijke inhoud van de kluis controleert, vertrouw je alleen op vermeldingen die iedereen in het juiste formaat zou kunnen schrijven.
Blockchain explorers en applicaties vertrouwen vaak op uitgezonden events om activiteiten zoals token transfers, NFT-bewegingen en andere statuswijzigingen bij te houden. Dit mechanisme introduceert echter een mogelijk beveiligingsprobleem: de mogelijkheid om misleidende transactierecords te genereren zonder daadwerkelijke transfer van assets. Deze post biedt een technische analyse van dit probleem, met praktische proof-of-concepts.
Het logging-systeem van de EVM stelt smartcontracts in staat om events uit te zenden via de volgende operaties:
LOG0
, LOG1
, LOG2
, LOG3
, en LOG4
opcodesemit
keyword en event
declaratiesCruciaal is dat niets een contract verhindert om willekeurige events met willekeurige parameters uit te zenden. Het blockchain-protocol zelf valideert niet of events nauwkeurig de statuswijzigingen weerspiegelen die ze beweren te vertegenwoordigen.
De ERC-20 specificatie vereist dat een Transfer event wordt uitgezonden wanneer een transfer plaatsvindt:
event Transfer(address indexed from, address indexed to, uint256 value);
Dit is echter een zachte vereiste, het gaat uit van een eerlijke implementatie. De EVM dwingt geen correlatie af tussen het Transfer event en de daadwerkelijke beweging van tokens.
De ERC-20 standaard specificeert dat contracten Transfer
events moeten uitzenden wanneer tokens van eigenaar veranderen.
De meeste blockchain explorers (waaronder Etherscan), wallets,
en analytics platforms volgen deze events om gebruikersinterfaces en transactiegeschiedenissen op te bouwen.
Een voorbeeld van een Solidity contract dat gebruikers in staat stelt om transacties op de blockchain te spoofen.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Spoofer { event Transfer(address indexed from, address indexed to, uint256 value); function spoofTransfer(address from, address to, uint256 amount) public { emit Transfer(from, to, amount); } }
En dit is met een willekeurige ERC-20 token naam en symbool.
pragma solidity ^0.8.0; contract Spoofer { string public name = "PocToken"; // Dit kan alles zijn zoals USDC, USDT, etc. string public symbol = "POC"; // Dit ook uint8 public decimals = 18; uint256 public totalSupply = 0; event Transfer(address indexed from, address indexed to, uint256 value); function spoofTransfer(address from, address to, uint256 amount) public { emit Transfer(from, to, amount); } }
Dit contract bevat geen daadwerkelijke token opslag of transfer logica. Toch, wanneer de spoofTransfer
functie wordt aangeroepen, zendt het een standaard-conforme ERC-20 Transfer
event uit dat blockchain explorers interpreteren als een legitieme token transfer.
Bekijk het hier Hier hebben we een transfer gespoofed van het USDC contract adres naar een willekeurig adres.
Hetzelfde principe geldt voor non-fungible tokens (NFTs) die de ERC-721 standaard volgen, waar Transfer
events eigendomsveranderingen signaleren.
pragma solidity ^0.8.0; contract FakeERC721TransferSimulator { string public name = "pocNFT"; string public symbol = "POC"; event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); function spoofSafeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external { // Je kunt 'data' verwerken of loggen indien nodig om de ABI-structuur te matchen emit Transfer(from, to, tokenId); } function ownerOf(uint256 tokenId) public pure returns (address) { return address(0); } function balanceOf(address owner) public pure returns (uint256) { return 0; } }
Met een echte POC transactie op BaseChain, die een NFT verstuurt vanaf Vitalik naar een willekeurig adres, kunnen we de events zien.
Bekijk het hier Hier hebben we een ERC-721 transfer gespoofed van Vitalik's adres naar een willekeurig adres.
In de EVM zijn logs onderdeel van de transaction receipt, niet van de interne opslag van het contract. Events kunnen niet worden gelezen vanuit het contract, noch hebben ze invloed op storage slots. Deze scheiding betekent dat het uitzenden van een event niet impliceert dat er daadwerkelijke berekening of token boekhouding heeft plaatsgevonden.
State wordt gewijzigd met SSTORE, en gelezen via SLOAD. Events daarentegen worden toegevoegd aan de logs array en ingevoegd in de bloom filter voor indexeringsdoeleinden. De EVM controleert niet of de gegevens in het event overeenkomen met daadwerkelijke opslagwijzigingen in dezelfde transactie.
balance
om gebruikers te misleiden bij het ondertekenen van kwaadaardige goedkeuringen.Ook onderzoekers moeten zich bewust zijn van dit probleem en er rekening mee houden bij het analyseren van transacties.