数组存储溢出AlienCodex

70 阅读2分钟

源码:

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

import './ownable.sol';

contract AlienCodex is Ownable {

  bool public contact;
  bytes32[] public codex;

  modifier contacted() {
    assert(contact);
    _;
  }
  
  function make_contact() 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;
  }
}

分析:

这个题要我们拿到owner,但是看又没有设置所有权的代码。这里就需要我们了解storage中的array underflow及如何计算array中元素于storage中的储存位置。

我们可以查一下slot里都存了些什么,由于合约继承了Ownable合约,所以slot0中存储的就是owner对象,slot1存储的则是codex动态数组,更准确来说,应该是codex动态数组的长度,而具体的下标内容呢?

image.png

会按序存储在keccak256(bytes(1))+x的插槽内,其中,x就是数组的下标。所以我们将插槽表示出来:

image.png

我们现在计算codex data的起始插槽:

image.png

我们先测试一下准确性。由于contacted modifier的存在,我们先修改contact变量。调用await contact.make_contact(),再次查看插槽数值,可以发现变量成功被修改。

image.png

现在我们就希望通过修改codexdata导致溢出最终修改slot 0.
执行retract,查看现在的slot[1]:

image.png

那下标该是多少呢?应该是2**256-1-0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6+1。因为我们到达末端后需要再进一位产生上溢出,返回slot0。
在remix上计算出卡槽位置: 35707666377435648211887908874984608119992236509074197713628505308453184860938, 需要传入0x000000000000000000000000DdC8C0c65Fe98f6D217a96EA9089236fC2Fe915A。修改地址,前面加一堆0是为了满足补齐24个0,凑足244+404=256位即32bytes,从而将地址存入正确的存储位置。 拿到数据后调用revise

image.png

查看owner,修改成功!!

image.png