TL;DR
This article is all about smart contract auditing tools. A smart contract audit entails a thorough examination of the contract's code to spot security flaws, improper, inefficient coding, and to decide how to fix the issues. In this article, we will take a look at some of the top smart contract auditing tools with a small demo
NOTE- Internal workings, architecture and advanced usage of all these tools can't be compiled in a single article, and for that I would work on single dedicated article for each of these tools.
Echidna
Echidna is a Haskell program designed for fuzzing/property-based testing of Ethereum smart contracts. It uses sophisticated grammar-based fuzzing campaigns based on a contract ABI to falsify user-defined predicates or Solidity assertions.
Fuzz testing or Fuzzing is the art of automatic bug detection. It is a Black Box software testing technique, which consists of finding implementation bugs using malformed/ semi-malformed data injection in an automated fashion.
In contrast to a classic fuzzer that will try to find crashes, Echidna will try to break user-defined invariants.
In smart contracts, invariants are Solidity functions, that can represent any incorrect or invalid state that the contract can reach, including:
Incorrect access control: the attacker became the owner of the contract.
Incorrect state machine: the tokens can be transferred while the contract is paused.
Incorrect arithmetic: the user can underflow its balance and get unlimited free tokens.
Demo
- For this demo we would be using a sample smart contract saved as Test.sol in any directory of your choice.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract Counter {
uint public count;
function inc() external {
count += 1;
}
function dec() external {
count -= 1;
}
}
contract TestCounter is Counter {
function echidna_test_true() public view returns (bool) {
return true;
}
function echidna_test_false() public view returns (bool) {
return false;
}
function echidna_test_count() public view returns (bool) {
// Here we are testing that Counter.count should always be <= 5.
return count <= 5;
}
}
In the folder where your contract is stored execute the following command.
docker run -it --rm -v $PWD:/code trailofbits/eth-security-toolbox
Now inside docker, your code will be stored at /code. After running the above commandcd /code
.Now you are inside '/code' folder present in docker container and execute command
echidna-test Test.sol --contract TestCounter
. This command runs the tests written inside the Test.sol file and if we have multiple contracts, then we need to specify the contract name using the--contract
tag.
NOTE- You can also select the desired compiler version by using the command solc-select use 'VERSION'
As we can see there were 3 tests and in any condition, if any of the tests return false then that test fails and Echidna also gives the transaction sequence due to which the test failed.
Slither
Slither is a Solidity static analysis framework written in Python3. It runs a suite of vulnerability detectors, prints visual information about contract details, and provides an API to easily write custom analyses. Slither enables developers to find vulnerabilities, enhance their code comprehension, and quickly prototype custom analyses.
Features
Automated vulnerability detection. A large variety of smart contract bugs can be detected without user intervention or additional specification effort.
Automated optimization detection. Slither detects code optimizations that the compiler misses.
Code understanding. Slither summarizes and displays contractsโ information to aid your study of the codebase.
Assisted code review. A user can interact with Slither through its API.
Demo
Steps 1 and 2 are the same but for this demo we would be using a sample voting smart contract saved as test.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract VotingSystem{
struct Voter{
bool alreadyVoted ;
uint proposalIndex;
}
struct Proposal{
string proposalName;
uint256 voteCount;
}
mapping(address => Voter) VoterAddress;
Proposal[] private proposals;
// input name of the proposal
function initialize(string memory name) public {
proposals.push(Proposal(name,0));
}
// msg.sender can vote only once
// this function finds the proposal with the passed string in input
function voteTo(string memory _name) public {
require(VoterAddress[msg.sender].alreadyVoted== false, "you have already voted");
VoterAddress[msg.sender].alreadyVoted = true;
for(uint i = 0;i<proposals.length;i++){
if(keccak256(bytes(proposals[i].proposalName)) == keccak256(bytes(_name))){
VoterAddress[msg.sender].proposalIndex = i;
proposals[i].voteCount++;
}
}
}
// this function returns who the winner is
function winner() view public returns(string memory){
uint max = 0;
string memory winnerProposal;
for(uint i = 0;i<proposals.length;i++){
if(proposals[i].voteCount>max){
max = proposals[i].voteCount;
winnerProposal = proposals[i].proposalName;
}
}
return winnerProposal;
}
}
For the next step we just need to run the command slither test.sol
Here we can see the recommendations and vulnerabilities pointed out by slither.
Mythril
Developed as a part of the MythX security analysis suite by Consensys, Mythril is the dynamic smart contract security analysis component of this toolset. Mythril uses symbolic execution, SMT solving, and taint analysis to detect security vulnerabilities with contracts on any EVM-compatible chain.
The Mythril tool can be used for discovering vulnerabilities such as numeric overflow, owner overwrite, function re-run, etc.
The tool provides a fairly detailed and user-friendly audit report. This makes it easy to find the highlighted part of the code in the original file and manually check for potential vulnerabilities. Mythril cannot find ways to save gas.
Demo
- First step would be to create a vulnerable contract and save it as
TimeLock.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
contract TimeLock {
mapping(address => uint) public balances;
mapping(address => uint) public lockTime;
function deposit() external payable {
balances[msg.sender] += msg.value;
lockTime[msg.sender] = block.timestamp + 1 weeks;
}
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease;
}
function withdraw() public {
require(balances[msg.sender] > 0, "Insufficient funds");
require(block.timestamp > lockTime[msg.sender], "Lock time not expired");
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
- Next step is to run the command
docker run -v $(pwd):/code mythril/myth analyze /code/EtherStore.sol
. This command mounts your code from the current directory inside your docker container, inside/code
folder and runs themythril analyze
command as well.
There are many tools out there but these are the ones that I personally recommend & that's it for now folks.....adios
Top comments (0)