Shop
// 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();
}
}
}
Goal
Buy the item from the shop for less than 100, make the value of the price variable inside Shop to be less than 100
Exploit
In order to finish this CTF we will have to use another smart contract. We can name this contract Attacker.
This CTF is somewhat similar to the Elevator one.
The Attacker contract should implement the price() function from the Buyer interface and return different value the second time this function is called.
This time we won't depend on additional variable (we used count in the Elevator CTF), but we will hack our way into the isSold variable which is set to false by default.
If we take a look in the if statement, when the buy() function is called
if (_buyer.price() >= price && !isSold) {
isSold = true;
price = _buyer.price();
}
when price() function is called for the first time from the Attacker contract, it should return value greater than 100, and inside the if, when the same function is called second time, it should return value less than 100, and therefore set the value of the price variable inside Shop to that value.
What we need to figure out is how to track wether price() is called for the first or second time.
This can be done with the use of the isSold variable. When we call price() function for the first time we know that isSold is false by default and we can return some value like 130 for example from our price function.
Before calling price() again, isSold variable is set to true, one line before, so inside price() we can check the value of the isSold variable, and if this value is true we return value less than 100
To exploit the contract we need to:
- create Attacker contract, and create instance from the Shop contract inside the Attacker contract
- create buyCheaper() external function inside Attacker, and call the buy() function inside Shop within
-
impelment/create price() function from Buyer interface, and inside
- if isSold variable from Shop contract is false, return value equal or greater than 100
- if isSold variable from Shop contract is true, return value less than 100
-
call buyCheaper() function from our account
Attacker contract code
contract Attacker {
Shop private immutable shop;
constructor(address _shop) {
shop = Shop(_shop);
}
function buyCheaper() external {
shop.buy();
}
function price() external view returns (uint) {
if(!shop.isSold()) {
return 130;
} else return 30;
}
}