Ethernaut 题库闯关 - Alien Codex 题解

Ethernaut 题库闯关追更, 挑战 AlienCodex 合约, 通过本挑战,我们将更深刻的理解 Solidity 的数据存储,以及如何通过写数组达到写插槽的效果。

Ethernaut 题库闯关 追更啦, 欢迎订阅专栏,本题是一个名为 AlienCodex 的合约,获取到 Ownable 权限即可通关。 难度等级:难。

本篇应该是 Ethernaut 系列最难的挑战,完成后非常有收获。

挑战题

以下是合约源码:

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

import '../helpers/Ownable-05.sol';

contract AlienCodex is Ownable {

  bool public contact;
  bytes32[] public codex;

  modifier contacted() {
    assert(contact);
    _;
  }

  function makeContact() public {
    contact = true;
  }

  function record(bytes32 _content) contacted public {
    codex.push(_content);
  }

  function retract() contacted public {
    codex.length--;
  }

  function revise(uint i, bytes32 _content) contacted public {
    codex[i] = _content;
  }
}

本关闯关涉及到 3 个知识点:

  • 了解数组存储的工作原理
  • 了解 ABI 规范
  • 了解一个非常隐秘且高级方法: 通过数组写任意插槽的数据,从而覆盖变量的值,非常的神奇。

如果你要挑战一下,就不要往下看了....

分析合约存储布局

首先我们要知道,所有 Solidity 状态变量都按顺序保存(动态数组和映射除外)。

AlienCodex 合约里有 3 个字段:

  1. ownable : 继承自Ownable合约(Ownable-05.sol 文件中没有其他任何内容), 占用 20 字节
  2. contact : bool 值, 占用 8 字节
  3. codex : bytes32 数组

前 2 个字段(ownablecontact)将共享第一个槽,即 0 号插槽。

关于 slot 插槽布局, 前面 Vault 闯关 Privacy 闯关 亦有介绍,可前往重温。

第三个属性(codex)位于第二个槽中:1。但它是一个动态数组,因此在第二个槽中的值只是数组的长度。要注意的是,数组长度始终为 uint256,因此它始终占用一个完整的槽。

对槽的读取和写入和对变量的操作是一样的。

读取某个槽的值,可以使用RPC eth_getStorageAt , 如:

curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_getStorageAt", "params": ["0x2ACDe8bc8567D49CF2Fe54999d4d4A1cd1a9fFEA", "0x0", "latest"], "id": 1}' http://localhost:8547

img

0x0 是要读取的插槽号。

还可以使用 [cast storage](https://learnblockchain.cn/docs/foundry/i18n/zh/referenc...

剩余50%的内容订阅专栏后可查看

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

0 条评论

请先 登录 后评论
Ethernaut CTF
Ethernaut CTF
信奉 CODE IS LAW.