C 354: Winning an Auction (10 pts)

What you need:

Background

We'll make an Auction contract with a logic flaw and exploit it.

Opening the Remix IDE

In Chrome, go to
https://remix.ethereum.org/

Making the Auction Contract

In the left pane, right-click contracts and click "New File", as shown below.

Type the name Auction.sol and press Enter.

A "Auction.sol" tab appears on the right side. Paste in this code, as shown below.

The Auction contract takes bids, and when a higher bid comes in, it refunds the lower bid and accepts the higher bid.

The Attacker contract has a malicious fallback function that refuses to accept the refund, preventing any higher bid from being accepted.

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

contract Auction {
    address payable public currentLeader;
    uint public highestBid;
    event logBid(address _address, uint _bid);

    function bid() public payable {
        emit logBid(msg.sender, msg.value);
        require(msg.value > highestBid);

        require(currentLeader.send(highestBid)); // Refund the old leader, if it fails then revert

        currentLeader = payable(msg.sender);
        highestBid = msg.value;
    }
}

contract Attacker {
    Auction auction_address;
    event LogFallback(uint count, uint balance);

    constructor(address auction) payable { auction_address = Auction(auction); }

    function win() public payable { auction_address.bid{value: msg.value}(); }

    fallback () payable external {
        revert();
    }

    function getBalance() public returns(uint) { 
        address _this = address(this);
        return _this.balance;
    }    

}

Compiling

On the left side, click the third icon, outlined in green in the image below. The "SOLIDITY COMPILER" appears. At the bottom, click the blue "Compile Auction.sol" button.

On the left side, the third icon now has an orange "2" on it indicating two warnings, as shown below.

Deploying the Auction Contract

On the left side, click the fourth icon from the top, outlined in red in the image below. The "DEPLOY & RUN TRANSACTIONS" pane opens.

Use an Environment of "JavaScript VM (London)". If the account balances are not all 100 Ether, switch to "JavaScript VM (Berlin)" and back to "JavaScript VM (London)" to refresh the blockchain to its initial state.

Select a CONTRACT of "Auction - contracts/Auction.sol", outlined in blue in the image below.

Click the orange Deploy button.

Deploying the Attacker Contract

At the lower left, under the "Deployed Contracts" heading, you see the "AUCTION" contract.

In that line, click the copy icon, outlined in green in the image below, to copy that contract's address.

Select a CONTRACT of "Attacker - contracts/Auction.sol", outlined in red in the image below.

Paste the address into the field next to the Deploy button.

Click the red Deploy button.

Using the Auction Contract

First we'll run a normal auction, with the contract operating correctly.

At the lower left, click the > next to "AUCTION" to expand that section, as shown below.

At the top left, enter a VALUE of "1 ether", outlined in green in the image below.

Notice the first few characters in the current ACCOUNT number, outlined in red in the image below. In that image, the account number begins with 5B3. At the lower left, click the red bid button.

At the lower left, click the blue-gray currentLeader button. The current leader is the ACCOUNT number you noted previously.

At the lower left, click the blue-gray highestBid button. The highest bid is 1000000000000000000 wei, or 1 Ether, as shown below.

At the top left, notice that this account balance has fallen to approximately 99 ether, outlined in red in the image below.

Making a Higher Bid

At the top left, select the second ACCOUNT, which has a balance of 100 ether, outlined in red in the image below.

At the top left, enter a VALUE of "2 ether", outlined in green in the image below.

At the lower left, click the red bid button.

At the top left, click the drop-down arrow on the right side of the ACCOUNT field.

The first account now has approximately 100 ether, and the second account has approximately 98 ether, outlined in red in the image below.

The Ether previously bid has been returned, and only the Ether from the highest bid has been transferred to the Auction contract.

At the lower left, click the blue-gray currentLeader button. The current leader is the second ACCOUNT number.

At the lower left, click the blue-gray highestBid button. The highest bid is 2 Ether, as shown below.

Attacking the Auction

At the lower left, click the > next to "ATTACKER" to expand that section, as shown below.

At the top left, select the third ACCOUNT, which has a balance of 100 ether, outlined in red in the image below.

At the top left, enter a VALUE of "3 ether", outlined in green in the image below.

At the lower left, click the red win button.

The third ACCOUNT balance falls to approximately 97 ether, outlined in red in the image below.

At the lower left, click the blue-gray currentLeader button. The current leader is not the third ACCOUNT shown at the top left, it's the account number of the ATTACKER contract, outlined in blue in the image below.

At the lower left, click the blue-gray highestBid button. The highest bid is 3 Ether, as shown below.

C 354.1 Bidding Higher (10 pts)

To see the effect of the attack, at the top left, select the first ACCOUNT.

At the top left, enter a VALUE of "4 ether", outlined in green in the image below.

At the lower left, click the red bid button.

The transaction fails, and the first account does not lose 4 ether.

The flag is covered by a green rectangle in the image below.

Fix

Removing the "require" from line 13, as shown below, stops the attack.

References

Ethereum Basics

Solidity Security: Comprehensive list of known attack vectors and common anti-patterns

Solidity Top 10 Common Issues
Common attacks in Solidity and how to defend against them

Posted 5-19-2021
Explanation augmented 10-22-21
Environment updated to Javascript Vm (London) on 10-26-21
Fix added 10-27-21