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.
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.