# Slither ![](https://i.imgur.com/SbzE5Sx.png) Slither is a Solidity static analysis framework written in Python 3. 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. ## How to install *Slither requires Python 3.6+ and solc, the Solidity compiler.* https://github.com/crytic/slither/wiki/Developer-installation ### Using Pip `pip3 install slither-analyzer` `sudo npm install -g solc` then install `solc-select` ``` python3 -m pip install solc-select solc-select versions solc-select use 0.8.10 ``` ## Detector Detector is severity detector which will warning or alarm you when it has detect some vulnerability. ref: https://github.com/crytic/slither/wiki/Detector-Documentation ### Q1: ``` contract BaseContract{ address owner; modifier isOwner(){ require(owner == msg.sender); _; } } contract DerivedContract is BaseContract{ address owner; constructor(){ owner = msg.sender; } function withdraw() isOwner() external{ msg.sender.transfer(this.balance); } } ``` ### State variable shadowing ``` contract BaseContract{ address owner; modifier isOwner(){ require(owner == msg.sender); _; } } contract DerivedContract is BaseContract{ address owner; constructor(){ owner = msg.sender; } function withdraw() isOwner() external{ msg.sender.transfer(this.balance); } } ``` `owner` of BaseContract is never assigned and the modifier `isOwner` does not work. ### Q2: ``` contract Memory { uint[1] public x; // storage function f() public { f1(x); f2(x); } function f1(uint[1] storage arr) internal { arr[0] = 1; } function f2(uint[1] arr) internal { arr[0] = 2; } } ``` ### Modifying storage array by value #### Description Detect arrays passed to a function that expects reference to a storage array #### Exploit Scenario: ``` contract Memory { uint[1] public x; // storage function f() public { f1(x); // update x f2(x); // do not update x } function f1(uint[1] storage arr) internal { // by reference arr[0] = 1; } function f2(uint[1] arr) internal { // by value arr[0] = 2; } } ``` Bob calls f(). Bob assumes that at the end of the call x[0] is 2, but it is 1. As a result, Bob's usage of the contract is incorrect. #### Recommendation Ensure the correct usage of memory and storage in the function parameters. Make all the locations explicit. ### Q3: ``` contract Uninitialized{ address owner = msg.sender; struct St{ uint a; } function func() { St st; st.a = 0x0; } } ``` ### Uninitialized storage variables #### Description An uninitialized storage variable will act as a reference to the first state variable, and can override a critical variable. #### Exploit Scenario: ``` contract Uninitialized{ address owner = msg.sender; struct St{ uint a; } function func() { St st; st.a = 0x0; } } ``` Bob calls `func`. As a result, owner is overridden to `0`. #### Recommendation Initialize all storage variables. ### Q4: ``` contract Token { function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } contract MyBank{ mapping(address => uint) balances; Token token; function deposit(uint amount) public{ token.transferFrom(msg.sender, address(this), amount); balances[msg.sender] += amount; } } ``` ### Unchecked transfer #### Description The return value of an external transfer/transferFrom call is not checked #### Exploit Scenario: ``` contract Token { function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } contract MyBank{ mapping(address => uint) balances; Token token; function deposit(uint amount) public{ token.transferFrom(msg.sender, address(this), amount); balances[msg.sender] += amount; } } ``` Several tokens do not revert in case of failure and return false. If one of these tokens is used in MyBank, deposit will not revert if the transfer fails, and an attacker can call deposit for free.. #### Recommendation Use SafeERC20, or ensure that the transfer/transferFrom return value is checked. ## Printers Printer will print contract information follow by printer format List of all printers from: https://github.com/crytic/slither/wiki/Printer-documentation ### Install dependencies `brew install graphviz` `brew install xdot` ### Print call graph - Generate `.dot` file ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print call-graph ``` ![](https://i.imgur.com/PPxtAYo.png) - Generate `.dot` file to `.png` file ``` dot contracts/AlphaStaking.sol.all_contracts.call-graph.dot -Tpng -o contracts/AlphaStaking.sol.png ``` ![](https://i.imgur.com/cw4YjI3.png) #### Print contract summary ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print contract-summary ``` ![](https://i.imgur.com/hC88MDe.png) #### Print human summary ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print human-summary ``` ![](https://i.imgur.com/RSk01YA.png) #### Print inheritance-graph * Use below command to generate graph ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print inheritance-graph ``` * Use `xdot` to view graph ``` xdot contracts/AlphaStaking.sol.inheritance-graph.dot ``` ![](https://i.imgur.com/FqzlZCC.png) #### Print data dependency Print the data dependencies of the variables Run below command ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print function-id ``` Result ``` Function extract(uint256) +---------------------------------+--------------------------------------+ | Variable | Dependencies | +---------------------------------+--------------------------------------+ | amount | [] | | Initializable._initialized | [] | | Initializable._initializing | [] | | ReentrancyGuard._NOT_ENTERED | [] | | ReentrancyGuard._ENTERED | [] | | ReentrancyGuard._status | [] | | AlphaStaking.STATUS_READY | [] | | AlphaStaking.STATUS_UNBONDING | [] | | AlphaStaking.UNBONDING_DURATION | [] | | AlphaStaking.WITHDRAW_DURATION | [] | | AlphaStaking.alpha | ['alpha'] | | AlphaStaking.governor | [] | | AlphaStaking.pendingGovernor | [] | | AlphaStaking.worker | [] | | AlphaStaking.totalAlpha | ['totalAlpha', 'amount', 'SafeMath'] | | AlphaStaking.totalShare | [] | | AlphaStaking.users | [] | +---------------------------------+--------------------------------------+ ``` #### Print function id Run below command ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --print function-id ``` Result ``` AlphaStaking: +-----------------------------+------------+ | Name | ID | +-----------------------------+------------+ | initialize(address,address) | 0x485cc955 | | setWorker(address) | 0xc26f6d44 | | setPendingGovernor(address) | 0xf235757f | | acceptGovernor() | 0xe58bb639 | | getStakeValue(address) | 0x96802374 | | stake(uint256) | 0xa694fc3a | | unbond(uint256) | 0x27de9e32 | | withdraw() | 0x3ccfd60b | | reward(uint256) | 0xa9fb763c | | skim(uint256) | 0x6939aaf5 | | extract(uint256) | 0x85b39782 | | STATUS_READY() | 0x20e5a06b | | STATUS_UNBONDING() | 0x146191f6 | | UNBONDING_DURATION() | 0xe59357a7 | | WITHDRAW_DURATION() | 0x77913322 | | alpha() | 0xdb1d0fd5 | | governor() | 0x0c340a24 | | pendingGovernor() | 0xe3056a34 | | worker() | 0x4d547ada | | totalAlpha() | 0xcd2d5e44 | | totalShare() | 0x026c4207 | | users(address) | 0xa87430ba | +-----------------------------+------------+ ``` ## ERC20 Conformance `slither-check-erc` will check that: * All the functions are present * All the events are present * Functions return the correct type * Functions that must be view are view * Events' parameters are correctly indexed * The functions emit the events * Derived contracts do not break the conformance ``` slither-check-erc contracts/Alpha.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin Alpha ``` then you will get the result ``` ## Check functions [✓] totalSupply() is present [✓] totalSupply() -> () (correct return value) [✓] totalSupply() is view [✓] balanceOf(address) is present [✓] balanceOf(address) -> () (correct return value) [✓] balanceOf(address) is view [✓] transfer(address,uint256) is present [✓] transfer(address,uint256) -> () (correct return value) [✓] Transfer(address,address,uint256) is emitted ... ``` ## Upgradeability Checks https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-v2 ``` slither-check-upgradeability contracts/AlphaStakingV2.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin AlphaStakingV2 --new-contract-filename contracts/AlphaStakingV3.sol --new-contract-name AlphaStakingV3 ``` Result ``` Different variables between AlphaStakingV2 (contracts/AlphaStakingV2.sol#9-144) and AlphaStakingV3 (contracts/AlphaStakingV3.sol#9-152) AlphaStakingV2.alpha (contracts/AlphaStakingV2.sol#33) AlphaStakingV3.alpha (contracts/AlphaStakingV3.sol#34) Different variables between AlphaStakingV2 (contracts/AlphaStakingV2.sol#9-144) and AlphaStakingV3 (contracts/AlphaStakingV3.sol#9-152) AlphaStakingV2.users (contracts/AlphaStakingV2.sol#39) AlphaStakingV3.users (contracts/AlphaStakingV3.sol#40) Reference: https://github.com/crytic/slither/wiki/Upgradeability-Checks#incorrect-variables-with-the-v2 ``` *Still found some false detected* ## Code Similarity https://github.com/crytic/slither/wiki/Code-Similarity-detector ## Slither with brownie 🐕‍🦺 ### Running Slither with Brownie hack * Due to the interfaces compilation issue, create symbolic link from `interfaces` folder to `contracts` folder ``` ln -s /staking-tier-contracts/interfaces /staking-tier-contracts/contracts ``` * Run `slither .` to analyze all contracts * Slither analyze vulnerability then show the result ![](https://i.imgur.com/GL7tspQ.png) ## Triage mode * Use Triage mode to select which log you want to show `slither . --triage` * Sliter will show a list of type of detector ![](https://i.imgur.com/uK0gfUo.png) * Select the type you want to hide e.g.`0, 2` to continue Type `All` if you want to hide all type of detectors * Slither will create `slither.db.json` for detector that is hided. * Delete element on `slither.db.json` to bring back detector ## Detector Selection * Use `slither --list-detectors` to show all detectors * Use `--detect <detector>` to show only the result of given detector ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --detect <detector-name> ``` e.g. ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --detect arbitrary-send,pragma ``` result ![](https://i.imgur.com/Bv6sFTl.png) * Use `--exclude` to exclude some detectors e.g. to exclude pragma ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --exclude pragma ``` e.g. to exclude low severity ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --exclude-low ``` ## Configuration file If you don't want to config all options on CLI command. You can create config file call `slither.config.json` then config your interested detector or printer as this format ``` { "detectors_to_run": "unused-state,costly-loop,external-function,naming-convention,pragma", "printers_to_run": "contract-summary,human-summary", "detectors_to_exclude": "", "exclude_informational": false, "exclude_low": false, "exclude_medium": false, "exclude_high": false, "json": "", "disable_color": false, "filter_paths": "file1.sol,file2.sol", "legacy_ast": false } ``` then run with below command. the result will print to result.json ``` slither contracts/AlphaStaking.sol --solc-remaps OpenZeppelin=/Users/$(whoami)/.brownie/packages/OpenZeppelin --json result.json ``` ### Running Slither with brownie is still have the problem as each issue isn't resolve: ![](https://i.imgur.com/nZQu9Ux.png) - Add Support for Brownie https://github.com/crytic/slither/issues/638 - Fix brownie issue https://github.com/crytic/crytic-compile/issues/165 - Brownie does not generate proper compilation artifacts for interfaces https://github.com/eth-brownie/brownie/issues/941 https://github.com/yearn/yearn-vaults/pull/473 ## Slither with Truffle 🍄 Slither can run with truffle project without to change anything Run `slither .` inside truffle project ![](https://i.imgur.com/D563nQq.png) ![](https://i.imgur.com/wEhSNkz.png) https://www.youtube.com/watch?v=Dxex3b-eMq0 ref: https://ethereum.org/en/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/ -------------------------------------------------- https://medium.com/coinmonks/static-analysis-of-smart-contracts-with-slither-github-actions-1e67e54ed8a7