Solidity中的selfdestruct
函数提供了一种合约终止和余额转移的机制,它可能会导致强制将以太注入到目标合约中,改变其状态并破坏其逻辑。
Example
solidityCopy code
pragma solidity ^0.8.20;
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");
}
}
在这个合约中它使用了address(this).balance
,这一步会很危险,因为攻击合约可以通过selfdestruct强制将余额转给这个合约,导致没人能赢这个游戏
缓解策略就是不依赖 address(this).balance
来保护关键游戏逻辑,从而防止攻击。相反,使用自我维护的状态变量来跟踪存入的以太币。
solidityCopy code
pragma solidity ^0.8.20;
contract EtherGame {
uint public targetAmount = 7 ether;
uint public balance;
address public winner;
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;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: balance}("");
require(sent, "Failed to send Ether");
}
}
攻击模板:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract SelfDestructable {
// `terminate` will make the contract uncallable
function terminate(address sendTo) public {
selfdestruct(payable(sendTo));
}
receive() external payable {
return;
}
}
https://immunebytes.com/blog/self-destruct-exploit-forced-ether-injection-in-solidity-contracts/
https://r4bbit.vercel.app/blog/force-feeding-attacks-in-solidity