Go to course navigation

9 - Security II.

Tutorial objectives

  • Advanced techniques in security auditing
  • Static analysis tools
  • Fuzz and property-based testing

Tutorial pre-requisites

Caution:

Computers in the laboratory don’t allow to install programs and save their state between login sessions. Please bring your own hardware for these tutorials.

Slither

Solidity static code analysis framework

Whitepaper: https://www.researchgate.net/publication/333700886_Slither_A_Static_Analysis_Framework_For_Smart_Contracts

Detectors

$ slither .

Useful printers

  • contract-summary - Print a summary of the contracts
  • function-summary - Print a summary of the functions
  • human-summary - Print a human-readable summary of the contracts
  • inheritance - Print the inheritance relations between contracts
  • modifiers - Print the modifiers called by each function
  • require - Print the require and assert calls of each function
  • variable-order - Print the storage order of the state variables

Example

$ slither . --print contract-summary

Woke

Detectors

$ woke detect .

or automatically in the Tools for Solidity VS Code extension.

Property-based fuzz testing

  • multiprocessing implementation
  • can automatically launch ipdb (IPython-enhanced debugger) on exception

Documentation: https://ackeeblockchain.com/woke/docs/1.2.1/fuzzer

Caution:

Woke fuzzer currently relies on the Brownie testing framework. Brownie is expected to be replaced by the Woke testing framework in Woke 2.0.

Terminology

  • flow - a single operation (a function) randomly picked from a set of operations by the fuzzer
  • invariant - a function executed after each flow, usually containing assertions
  • sequence - a sequence of flows and invariants; after each sequence, the blockchain state is reset
  • campaign - an object allowing to define number of sequences and number of flows in each sequence; automatically runs flows and invariants

Each flow can be assigned a weight, which determines the probability of the flow being picked. The default weight is 100.

Example

contract Counter {
    uint256 public count;

    function increment() public {
        count += 1;
    }

    function decrement() public {
        count -= 1;
    }
}
import brownie
from woke.fuzzer.campaign import Campaign
from woke.fuzzer.decorators import flow, invariant, weight
from woke.fuzzer.random import random_account


class Test:
    def __init__(self, contract_type) -> None:
        self.owner = random_account()
        self.contract = contract_type.deploy({"from": self.owner})
        self.counter_value = 0

    @flow
    def flow_increment(self):
        self.contract.increment({"from": random_account()})
        self.counter_value += 1

    @flow
    @weight(101)
    def flow_decrement(self):
        if self.counter_value == 0:
            with brownie.reverts():
                self.contract.decrement({"from": random_account()})
        else:
            self.contract.decrement({"from": random_account()})
            self.counter_value -= 1

    @invariant
    def invariant_counter_value(self):
        assert self.contract.count() == self.counter_value


def test_counter(Counter):
    campaign = Campaign(lambda: Test(Counter))
    campaign.run(100, 40)

Echidna

Haskell program designed for fuzzy testing of Ethereum smart contracts

Whitepaper: https://www.researchgate.net/publication/343048222_Echidna_effective_usable_and_fast_fuzzing_for_smart_contracts

Example

$ echidna-test helloworld.sol --contract TEST --config echidna-config.yaml