源码:
// 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
动态数组的长度,而具体的下标内容呢?
会按序存储在keccak256(bytes(1))+x
的插槽内,其中,x就是数组的下标。所以我们将插槽表示出来:
我们现在计算codex data的起始插槽:
我们先测试一下准确性。由于contacted modifier
的存在,我们先修改contact
变量。调用await contact.make_contact()
,再次查看插槽数值,可以发现变量成功被修改。
现在我们就希望通过修改codex
的data
导致溢出最终修改slot 0.
执行retract,查看现在的slot[1]:
那下标该是多少呢?应该是2**256-1-0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6+1
。因为我们到达末端后需要再进一位产生上溢出,返回slot0。
在remix上计算出卡槽位置: 35707666377435648211887908874984608119992236509074197713628505308453184860938,
需要传入0x000000000000000000000000DdC8C0c65Fe98f6D217a96EA9089236fC2Fe915A
。修改地址,前面加一堆0是为了满足补齐24个0,凑足244+404=256位即32bytes,从而将地址存入正确的存储位置。
拿到数据后调用revise
查看owner,修改成功!!