Using Smart contract Audit tools

Using Smart contract Audit tools

While the Manual review process is irreplaceable, to speed up the process, audit tools come in handy. Over the deployment of smart contracts, through attacks done on many live projects, a list of vulnerabilities has been identified and all new projects mustn't commit the same mistakes as others did in the past.

SWC is an important repository of Vulnerabilities found in smart contract code from the past and the preventive measure one should take to not repeat the mistake.

https://swcregistry.io/

Tools

Using tools will help speed up the review process and identify any vulnerabilities. Most of the developer teams use these tools to quickly identify and monitor any such vulnerabilities. While there is a huge array of tools currently available in the market,

we shall look at trailofbits Eth-security-box and use them in the identification of vulnerabilities in some sample contracts listed in swcregistry.io.

First clone the docker image from the above link.

https://hub.docker.com/r/trailofbits/eth-security-toolbox

In this toolbox, you will have

  • Echidna property-based fuzz tester

  • Etheno integration tool and differential tester

  • Manticore symbolic analyzer and formal contract verifier

  • Slither static analysis tool

  • Rattle EVM lifter

Slither

Sample Contract for analysis

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

contract EtherStore {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);

        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }

    // Helper function to check the balance of this contract
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

Run Slither in the docker to evaluate the above contract as below.

ethsec@406c6dfed14b:/tools/contracts$ slither reentrancy.sol

Result of analysis

Slither identified Reentrancy Vulnerability in the above contract.

Reentrancy in EtherStore.withdraw() (reentrancy.sol#11-19): External calls: - (sent) = msg.sender.call{value: bal}() (reentrancy.sol#15) State variables written after the call(s): - balances[msg.sender] = 0 (reentrancy.sol#18) Reference: github.com/crytic/slither/wiki/Detector-Doc..

Pragma version^0.8.9 (reentrancy.sol#2) necessitates a version too recent to be trusted. Consider deploying with 0.6.12/0.7.6/0.8.7 solc-0.8.9 is not recommended for deployment Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity

Low level call in EtherStore.withdraw() (reentrancy.sol#11-19): - (sent) = msg.sender.call{value: bal}() (reentrancy.sol#15) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls

deposit() should be declared external: - EtherStore.deposit() (reentrancy.sol#7-9) withdraw() should be declared external: - EtherStore.withdraw() (reentrancy.sol#11-19) getBalance() should be declared external: - EtherStore.getBalance() (reentrancy.sol#22-24) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external reentrancy.sol analyzed (1 contracts with 78 detectors), 7 result(s) found

Manticore

The manticore verifier will automatically verify those invariants. manticore-verifier reduces the initial effort and cost involved in symbolic testing of arbitrary properties.

Example

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

contract Ownership{                                                                                                                         
        address owner = msg.sender;                                                                                                         
        function Onwer() public{                                                                                                            
                owner = msg.sender;                                                                                                         
        }                                                                                                                                   
        modifier isOwner(){                                                                                                                 
                require(owner == msg.sender);                                                                                               
                _;                                                                                                                          
        }                                                                                                                                   
}                                                                                                                                           
contract Pausable is Ownership{                                                                                                             
    bool is_paused;                                                                                                                         
    modifier ifNotPaused(){                                                                                                                 
        require(!is_paused);                                                                                                                
        _;                                                                                                                                  
    }                                                                                                                                       
    function paused() isOwner public{                                                                                                       
        is_paused = true;                                                                                                                   
    }                                                                                                                                       
    function resume() isOwner public{                                                                                                       
        is_paused = false;                                                                                                                  
    }                                                                                                                                       
}   

contract Token is Pausable{                                                                                                                 
    mapping(address => uint) public balances;                                                                                               
    function transfer(address to, uint value) ifNotPaused public{                                                                           
        balances[msg.sender] -= value;                                                                                                      
        balances[to] += value;                                                                                                              
    }                                                                                                                                       
}

and test the contract as below.

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

import "./token.sol";

contract TestToken is Token{                                                                                                                
        constructor() public{                                                                                                               
                // here lets initialize it to a known correct state                                                                         
                balances[msg.sender] = 10000; // deployer account owns all the tokens (10000)                                               
        }                                                                                                                                   
        // Now a property. Manticore will report any property method returning false                                                        
        function crytic_test_balance() view public returns(bool){                                                                           
                // No account could have more than the total ammount of tokens!                                                             
                return balances[msg.sender] <= 10000;                                                                                       
        }                                                                                                                                   
}

$manticore-verifier dapp.sol --contract TestToken # Owner account: 0x28e9eb58c2f5be87161a261f412a115eb85946d9 # Contract account: 0x9384027ebe35100de8ef216cb401573502017f7 # Sender_0 account: 0xad5e556d9699e9e35b3190d76f75c9bf9997533b # PSender account: 0xad5e556d9699e9e35b3190d76f75c9bf9997533b # Found 1 properties: crytic_test_balance # Exploration will stop when some of the following happens: # * 3 human transaction sent # * Code coverage is greater than 100% measured on target contract # * No more coverage was gained in the last transaction # * At least 1 different properties where found to be breakable. (1 for fail fast) # * 240 seconds pass # Starting exploration... Transaction 0. States: 1, RT Coverage: 0.0%, Failing properties: 0/1 Transaction 1. States: 2, RT Coverage: 60.66%, Failing properties: 0/1 Found 1/1 failing properties. Stopping exploration. 60.66% EVM code covered +---------------------+------------+ | Property Named | Status | +---------------------+------------+ | crytic_test_balance | failed (0) | +---------------------+------------+ Checkout testcases here:./mcore_kkgtybqb

Mythril

Mythril is a security analysis tool for EVM bytecode. It detects security vulnerabilities in smart contracts built for Ethereum.

Setup

Get it with Docker:

$ docker pull mythril/myth Install from Pypi (Python 3.6-3.9):

$ pip3 install mythril

Usage

Evaluate a smart contract as below.

myth analyze reentrancy.sol

Report generated by mythril

The Mythril identifies the reentrancy vulnerability in the report above.