今日已更新 412 条资讯 | 累计 19972 条内容
关于我们

Reentrancy in 2026: Why It Still Drains Millions (and How AI Spots It)

Pavel Espitia 2026年06月10日 23:17 5 次阅读 来源:Dev.to

Reentrancy is the oldest trick in smart contract exploitation. The DAO fell to it in 2016. You would think a bug this famous would be extinct by now. It isn't. Protocols still lose millions to reentrancy every year, because the textbook version is the easy one, and attackers stopped using the textbook version a long time ago. This post walks through the classic bug, then the modern variants that still bite in 2026: cross-function, cross-contract, read-only reentrancy through view functions, and callback reentrancy through ERC777 and ERC721. I'll show why ReentrancyGuard is not the silver bullet most developers think it is, and how an LLM-based auditor reasons about the call-then-state-change pattern in a way pattern matchers can't. The Classic: Single-Function Reentrancy Here is the canonical vulnerable withdraw. contract VulnerableVault { mapping(address => uint256) public balances; function deposit() external payable { balances[msg.sender] += msg.value; } function withdraw() external { uint256 amount = balances[msg.sender]; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "transfer failed"); balances[msg.sender] = 0; // state update AFTER the external call } } The attack is simple. The attacker is a contract. When withdraw sends ETH via msg.sender.call , control passes to the attacker's receive() function before balances[msg.sender] is zeroed. The attacker calls withdraw again. The balance is still the original amount, so the vault pays out again. Repeat until the vault is empty. contract Attacker { VulnerableVault vault; receive() external payable { if (address(vault).balance >= 1 ether) { vault.withdraw(); // re-enter before balance is zeroed } } } The fix is checks-effects-interactions. Do all your checks, then update all your state, and only then make the external call. function withdraw() external { uint256 amount = balances[msg.sender]; balances[msg.sender] = 0; // effect first (bool success, ) = msg.sender.call{value: amount}(""); //

本文内容来源于互联网,版权归原作者所有
查看原文