Ethereum vulnerabilities

Ethereum is the oldest smart contract platform. Many smart contracts built on it. More opportunity to identify vulnerabilities

An ethereum-specific vulnerabilities have been identified and exploited:

  • Denial of Service

  • Reentrancy

  • Short addresses

  • Unchecked return values

Denial of Service

function selectNextWinners(uint256 _largestWinner){
    for (uint256 i = 0; i < largestWinner; i++){
        // heavy code
    }
    largestWinner = _largestWinner;
}

Ethereum uses the concept of gas to protect against spam and other attacks. Every smart contract instruction requires gas to run, if a transaction runs out of gas before it completes, the entire execution is undone

Ethereum blocks have a set maximum gas value. Vulnerable smart contracts can be rendered completely unrunnable

Reentrancy

Ethereum smart contracts are commonly designed to send value between accounts

If an Ethereum smart contract is sent value, it is given the opportunity to run some code. Enables it to update its internal ledger, take action, etc.

Reentrancy vulnerabilities can exist if the receiving contract calls the sending contract. The sending contract may not have updated its internal state prior to sending value

The logic behind the code in fallback functions is good, however they can be vulnerable to exploitation

function withdraw(uint _amount) {
    require(balances[msg.sender] >= amount);
    msg.sender.call.values(_amount)();
    balances[msg.sender] -= _amount;
}

Logically the code makes sense, however the flow leaves it open to vulnerability

An attacker exploiting the reentrancy calls the withdrawal function multiple times before transactions are recorded

Short addresses

Ethereum right-pads function arguments if they are too short

This can create a vulnerability if a smart contract inconsistently specifies argument lengths.

  • E.g., address (fixed length) versus uint (set maximum length)

event Transfer(address _from, address indexed _to, uint256 _value);
function sendCoin(address to, uint amount) returns(bool sufficient) {
    if (balances[msg.sender] < amount) return false;
    balances[msg.sender] -= amount;
    balances[to] += amount;
    Transfer(msg.sender, to, amount);
    return true;   
}

Unchecked return values

Ethereum's low-level functions have inconsistent behavior when an error occurs, some throw an exception, some return false.

A failure to check function return values can leave a function vulnerable

function withdraw(uint _amount) public{
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] -= _amount;
    etherLeft -= _amount;
    msg.sender.send(_amount);
}

Last updated