Link Search Menu Expand Document

Broken Authorization in Smart Contracts

Play SecureFlag Play Smart Contracts Labs on this vulnerability with SecureFlag!

Writing correct authorization controls is critical when developing smart contracts. Access control governs balance transfer, token mint, votes, and many other things.

A malicious actor exploiting authorization weaknesses may be able to affect the integrity and availability of the contract, even going so far as to conduct unauthorized withdrawals.

Function Default Visibility

Visibility specifiers determine whether a user can invoke a given function. However, when this specifier is not explicitly applied, the visibility defaults to public, thus allowing unauthorized users to call the function.

This vulnerability has been exploited in a real-world attack in what is understood to be the first Parity multi-sig hack when an attacker was able to reset the ownership of the contract and drain $31M worth of Ether from three wallets.

Vulnerable Example

The following vulnerable contract is meant to transfer the balance to winners with a specific address only. Instead, it allows anyone to directly invoke the _sendWinnings() function and withdraw the balance.

/*
 * @source: https://github.com/sigp/solidity-security-blog#visibility
 * @author: SigmaPrime 
 * Modified by Gerhard Wagner
 */

pragma solidity ^0.4.24;

contract HashForEther {

    function withdrawWinnings() {
        // Winner if the last 8 hex characters of the address are 0. 
        require(uint32(msg.sender) == 0);
        _sendWinnings();
     }

     function _sendWinnings() {
         msg.sender.transfer(this.balance);
     }
}

Fixed Example

Explicitly set the internal visibility specifier to protect the vulnerable function so that it can only be accessed internally, i.e., from within the current contract or contracts deriving from it.

     function _sendWinnings() internal{
         msg.sender.transfer(this.balance);
     }

Prevention

Functions can be specified as being either external, public, internal or private. Developers must make an informed, conscious decision about which visibility type is appropriate for which function, taking into account the high stakes when opening a function to public.

Incorrect Constructor Name

Incorrect Constructor Name vulnerabilities occur when a particular type of special function, known as a constructor, is named incorrectly or is left with the same name following a contract name change. If this disparity between contract name and constructor name occurs in development, the constructor will revert to having runtime, normal functions.

This vulnerability affected Solidity version before 0.4.22, while more recent versions require the constructor to be defined with the constructor keyword, effectively mitigating this vulnerability.

The exploit of this vulnerability results in a publicly callable function and allows malicious actors to perform unintended or unauthorized actions.

This vulnerability has been exploited in a real-world attack against the Rubixi contract, resulting in the contract fee being stolen because anyone was able to become the owner of the contract… and someone did.

Vulnerable example

The following vulnerable contract designates the address which initializes it as the contract’s owner. This is a common pattern adopted to grant special privileges, such as the ability to withdraw contract funds.

/*
 * @source: https://github.com/crytic/not-so-smart-contracts/blob/master/wrong_constructor_name/incorrect_constructor.sol
 */

pragma solidity ^0.4.15;

contract Missing{
    address private owner;

    modifier onlyowner {
        require(msg.sender==owner);
        _;
    }

    // The name of the constructor should be Missing
    // Anyone can call the IamMissing once the contract is deployed
    function IamMissing()
        public 
    {
        owner = msg.sender;
    }

    function withdraw() 
        public 
        onlyowner
    {
       owner.transfer(this.balance);
    }
}

Fixed example

Developers should rewrite the contract to use the constructor keyword.

    constructor() public 
    {
        owner = msg.sender;
    }

Prevention

Use constructor instead of a named constructor to prevent this issue.

References

Solidity - Visibility and Getters Solidity Security Blog - Default Visibilities Smart Contract Weakness Classification and Test Cases - Function Default Visibility DASP - Access Control Solidity - Creating Contracts Ethereum Blog - Thinking About Smart Contract Security