├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── github_results.sh ├── old │ ├── github_results.sh │ ├── results-ubuntu.csv │ └── ubuntu.yml ├── results-ubuntu-hx.csv ├── results-ubuntu-rt.csv ├── results-ubuntu-sol.csv └── ubuntu.yml ├── .gitignore ├── LICENSE ├── README.md ├── doc ├── academia.md ├── datasets.md ├── installation.md └── usage.md ├── install ├── docker_test.py ├── requirements-3.10.txt ├── requirements-3.11.txt ├── requirements-3.12.txt ├── requirements-3.6.txt ├── requirements-3.7.txt ├── requirements-3.8.txt ├── requirements-3.9.txt └── setup-venv.sh ├── reparse ├── results2csv ├── samples ├── BecToken.hex ├── BecToken.rt.hex ├── BecToken.sol ├── ERC20.hex ├── ERC20.rt.hex ├── ERC20.sol ├── EtherLotto.hex ├── EtherLotto.rt.hex ├── EtherLotto.sol ├── Government.hex ├── Government.rt.hex ├── Government.sol ├── MyToken.hex ├── MyToken.rt.hex ├── MyToken.sol ├── OpenAddressLottery.hex ├── OpenAddressLottery.rt.hex ├── OpenAddressLottery.sol ├── ReturnValue.hex ├── ReturnValue.rt.hex ├── ReturnValue.sol ├── Rubixi.hex ├── Rubixi.rt.hex ├── Rubixi.sol ├── SimpleDAO.hex ├── SimpleDAO.rt.hex ├── SimpleDAO.sol ├── SmartBillions.hex ├── SmartBillions.rt.hex ├── SmartBillions.sol └── vulnerabilities.json ├── sb ├── __init__.py ├── __main__.py ├── analysis.py ├── cfg.py ├── cli.py ├── colors.py ├── docker.py ├── errors.py ├── io.py ├── logging.py ├── parse_utils.py ├── parsing.py ├── reparse.py ├── results2csv.py ├── sarif.py ├── settings.py ├── smartbugs.py ├── solidity.py ├── tasks.py ├── tools.py └── utils.py ├── site_cfg.yaml ├── smartbugs ├── solcx ├── __init__.py ├── exceptions.py ├── install.py ├── main.py ├── utils │ ├── __init__.py │ └── lock.py └── wrapper.py ├── templates ├── results.sql ├── scripts │ └── example.py └── tools │ └── template │ ├── config.yaml │ ├── parser.py │ └── scripts │ └── printContractNames.py └── tools ├── all └── config.yaml ├── confuzzius ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_solidity.sh │ └── printContractNames.py ├── conkas ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── ethainter ├── config.yaml ├── docker │ ├── Dockerfile │ └── scripts │ │ └── run.sh ├── findings.yaml ├── parser.py └── scripts │ └── do_runtime.sh ├── ethor-2021 ├── config.yaml ├── docker │ ├── README.md │ ├── dockertools.nix │ ├── ethor-with-reconstruction.nix │ ├── precompiled-ethor.nix │ ├── reconstructCfg.nix │ └── z3.nix ├── findings.yaml └── parser.py ├── ethor-2023 ├── config.yaml ├── docker │ ├── README.md │ ├── dockertools.nix │ ├── ethor-with-reconstruction.nix │ ├── flake.lock │ ├── flake.nix │ ├── precompiled-ethor.nix │ ├── reconstructCfg.nix │ ├── reconstruction.patch │ └── z3.nix ├── findings.yaml └── parser.py ├── ethor └── config.yaml ├── gigahorse └── parser.py ├── honeybadger ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── madmax ├── config.yaml ├── docker │ ├── Dockerfile │ └── scripts │ │ ├── gitconfig │ │ └── run.sh ├── findings.yaml ├── parser.py └── scripts │ └── do_runtime.sh ├── maian ├── config.yaml ├── docker │ ├── Dockerfile │ ├── README.txt │ └── scripts │ │ ├── printContractNames.py │ │ ├── runMAIANall.sh │ │ ├── run_maian_bytecode.sh │ │ └── run_maian_solidity.sh ├── findings.yaml ├── parser.py └── scripts │ ├── do_bytecode.sh │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── manticore-0.3.7 ├── config.yaml ├── parser.py └── scripts │ ├── do_solidity.sh │ └── printContractNames.py ├── manticore └── config.yaml ├── mythril-0.23.15 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_bytecode.sh │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── mythril-0.23.5 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_bytecode.sh │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── mythril-0.24.7 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_bytecode.sh │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── mythril └── config.yaml ├── osiris ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── oyente ├── config.yaml ├── docker │ └── README.txt ├── findings.yaml ├── parser.py └── scripts │ ├── do_runtime.sh │ ├── do_solidity.sh │ └── printContractNames.py ├── pakala ├── config.yaml ├── docker │ ├── Dockerfile │ └── scripts │ │ └── runPakala.sh ├── findings.yaml ├── parser.py └── scripts │ └── do_runtime.sh ├── securify ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_runtime.sh │ └── do_solidity.sh ├── semgrep ├── Docker │ └── Dockerfile ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── sfuzz ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ ├── do_solidity.sh │ └── printContractNames.py ├── slither-0.10.0 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── slither-0.10.4 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── slither-0.6.1 ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── slither └── config.yaml ├── smartcheck ├── config.yaml ├── findings.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── solhint-2.1.0 ├── config.yaml ├── parser.py └── scripts │ └── do_solidity.sh ├── solhint-3.3.8 ├── config.yaml ├── docker │ ├── .solhint.json │ ├── Dockerfile │ └── docker-entrypoint.sh ├── parser.py └── scripts │ └── do_solidity.sh ├── solhint └── config.yaml ├── teether ├── config.yaml ├── docker │ └── Dockerfile ├── findings.yaml └── parser.py └── vandal ├── config.yaml ├── docker ├── Dockerfile └── scripts │ └── runVandal.sh ├── findings.yaml ├── parser.py └── scripts └── do_runtime.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Template for bug reports 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A description of what the bug is: what did you do, what did you expect, what did actually happen. 12 | If applicable, add screenshots to help explain your problem. 13 | 14 | **Platform** 15 | Please provide information on the version of SmartBugs and your platform. Run SmartBugs with the option `--version` to obtain this information. 16 | 17 | - SmartBugs version: [e.g. 2.0.2] 18 | - Python version: [e.g. 3.10.6] 19 | - OS : [e.g. Linux, Ubuntu, MacOS, Windows, ...] 20 | - CPU: [e.g. Intel, AMD, M1, ... ] 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/github_results.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run .github/github_results.sh from SmartBugs home directory 4 | # Generates .github/results-ubuntu.csv, needed for the workflow ubuntu.yml 5 | # as a reference for comparing the results of the workflow with 6 | 7 | #rm -rf results/*/github-sol 8 | ./smartbugs -t all -f 'samples/SimpleDAO.sol' --runid github-sol --sarif --main --timeout 180 9 | ./results2csv -x start duration -- results/*/github-sol | sed '/confuzzius/s/".*"//' > .github/results-ubuntu-sol.csv 10 | 11 | #rm -rf results/*/github-rt 12 | ./smartbugs -t all -f 'samples/SimpleDAO.rt.hex' --runid github-rt --sarif --timeout 180 13 | ./results2csv -x start duration -- results/*/github-rt > .github/results-ubuntu-rt.csv 14 | 15 | #rm -rf results/*/github-hx 16 | ./smartbugs -t all -f 'samples/SimpleDAO.hex' --runid github-hx --sarif --timeout 180 17 | ./results2csv -x start duration -- results/*/github-hx > .github/results-ubuntu-hx.csv 18 | -------------------------------------------------------------------------------- /.github/old/github_results.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run .github/github_results.sh from SmartBugs home directory 4 | # Generates .github/results-ubuntu.csv, needed for the workflow ubuntu.yml 5 | # as a reference for comparing the results of the workflow with 6 | 7 | rm -rf results/github 8 | ./smartbugs -t all -f 'samples/SimpleDAO.*' --runid github --json --main --timeout 360 9 | ./results2csv -x start duration -- results/*/github | sed '/confuzzius/s/".*"//' > .github/results-ubuntu.csv 10 | -------------------------------------------------------------------------------- /.github/results-ubuntu-hx.csv: -------------------------------------------------------------------------------- 1 | filename,basename,toolid,toolmode,parser_version,runid,exit_code,findings,infos,errors,fails 2 | samples/SimpleDAO.hex,SimpleDAO.hex,maian,bytecode,2022/11/11,github-hx,0,"No_Ether_lock_Ether_refused,Not_destructible_no_self_destruct",,, 3 | samples/SimpleDAO.hex,SimpleDAO.hex,mythril-0.23.15,bytecode,2023/01/20,github-hx,1,"Exception_State_SWC_110,External_Call_To_User_Supplied_Address_SWC_107,State_access_after_external_call_SWC_107,Unchecked_return_value_from_external_call_SWC_104,Unprotected_Ether_Withdrawal_SWC_105",,, 4 | -------------------------------------------------------------------------------- /.github/results-ubuntu-rt.csv: -------------------------------------------------------------------------------- 1 | filename,basename,toolid,toolmode,parser_version,runid,exit_code,findings,infos,errors,fails 2 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,conkas,runtime,2022/11/11,github-rt,0,"Integer_Overflow,Integer_Underflow,Reentrancy,Unchecked_Low_Level_Call",,, 3 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,ethainter,runtime,2022/11/17,github-rt,0,,,, 4 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,ethor,runtime,2022/11/11,github-rt,0,insecure,Encountered an unknown bytecode,, 5 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,honeybadger,runtime,2023/02/27,github-rt,0,Money_flow,,, 6 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,madmax,runtime,2022/11/17,github-rt,0,,,, 7 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,maian,runtime,2022/11/11,github-rt,0,"No_Ether_lock_Ether_refused,Not_destructible_no_self_destruct",,, 8 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,mythril-0.23.15,runtime,2023/01/20,github-rt,1,"Exception_State_SWC_110,External_Call_To_User_Supplied_Address_SWC_107,State_access_after_external_call_SWC_107,Unchecked_return_value_from_external_call_SWC_104,Unprotected_Ether_Withdrawal_SWC_105",,, 9 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,osiris,runtime,2023/02/27,github-rt,0,"Callstack_bug,Overflow_bugs,Reentrancy_bug,Underflow_bugs",,, 10 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,oyente,runtime,2023/02/27,github-rt,1,"Callstack_Depth_Attack_Vulnerability,Re_Entrancy_Vulnerability",,, 11 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,pakala,runtime,2023/02/27,github-rt,0,,,, 12 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,securify,runtime,2022/11/17,github-rt,0,"DAO,MissingInputValidation,UnhandledException",,, 13 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,teether,runtime,2023/02/24,github-rt,0,,Could not exploit any RETURN+CALL,, 14 | samples/SimpleDAO.rt.hex,SimpleDAO.rt.hex,vandal,runtime,2023/02/27,github-rt,0,"ReentrantCall,UncheckedCall",Warning: Deprecated type declaration,, 15 | -------------------------------------------------------------------------------- /.github/results-ubuntu-sol.csv: -------------------------------------------------------------------------------- 1 | filename,basename,toolid,toolmode,parser_version,runid,exit_code,findings,infos,errors,fails 2 | samples/SimpleDAO.sol,SimpleDAO.sol,confuzzius,solidity,2022/12/31,github-sol,0,,,, 3 | samples/SimpleDAO.sol,SimpleDAO.sol,conkas,solidity,2022/11/11,github-sol,0,"Integer_Overflow,Reentrancy,Unchecked_Low_Level_Call",,, 4 | samples/SimpleDAO.sol,SimpleDAO.sol,honeybadger,solidity,2023/02/27,github-sol,0,Money_flow,,, 5 | samples/SimpleDAO.sol,SimpleDAO.sol,maian,solidity,2022/11/11,github-sol,0,"No_Ether_lock_Ether_refused,Not_destructible_no_self_destruct",,, 6 | samples/SimpleDAO.sol,SimpleDAO.sol,manticore-0.3.7,solidity,2022/11/17,github-sol,0,,,,"exception (manticore.core.state.Concretize),exception (manticore.exceptions.ManticoreError: Forking on unfeasible constraint set)" 7 | samples/SimpleDAO.sol,SimpleDAO.sol,mythril-0.23.15,solidity,2023/01/20,github-sol,1,"External_Call_To_User_Supplied_Address_SWC_107,State_access_after_external_call_SWC_107,Unchecked_return_value_from_external_call_SWC_104,Unprotected_Ether_Withdrawal_SWC_105",,, 8 | samples/SimpleDAO.sol,SimpleDAO.sol,osiris,solidity,2023/02/27,github-sol,0,"Callstack_bug,Overflow_bugs,Reentrancy_bug,Underflow_bugs",,, 9 | samples/SimpleDAO.sol,SimpleDAO.sol,oyente,solidity,2023/02/27,github-sol,1,"Callstack_Depth_Attack_Vulnerability,Integer_Overflow,Re_Entrancy_Vulnerability",,, 10 | samples/SimpleDAO.sol,SimpleDAO.sol,securify,solidity,2022/11/17,github-sol,0,"DAO,MissingInputValidation,UnhandledException",,, 11 | samples/SimpleDAO.sol,SimpleDAO.sol,sfuzz,solidity,2023/03/02,github-sol,0,"Exception_Disorder,Reentrancy",,, 12 | samples/SimpleDAO.sol,SimpleDAO.sol,slither,solidity,2022/11/14,github-sol,6,"external_function,low_level_calls,reentrancy_eth,solc_version",,EXIT_CODE_6, 13 | samples/SimpleDAO.sol,SimpleDAO.sol,smartcheck,solidity,2022/11/14,github-sol,0,"SOLIDITY_CALL_WITHOUT_DATA,SOLIDITY_PRAGMAS_VERSION,SOLIDITY_UPGRADE_TO_050,SOLIDITY_VISIBILITY",,, 14 | samples/SimpleDAO.sol,SimpleDAO.sol,solhint-3.3.8,solidity,2023/02/12,github-sol,1,"avoid_call_value,avoid_low_level_calls,compiler_version,func_visibility,no_unused_vars",,EXIT_CODE_1, 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Ignore results 2 | results/ 3 | 4 | #Ignore DS_Store 5 | .DS_Store 6 | 7 | #Ignore pycache 8 | *.pyc 9 | __pycache__ 10 | 11 | #Ignore vscode 12 | .vscode/ 13 | 14 | #Ignore default remote datasets 15 | dataset/solidiFI 16 | 17 | #Ignore PyCharm files 18 | venv/ 19 | .idea/ 20 | results/*.* 21 | -------------------------------------------------------------------------------- /doc/datasets.md: -------------------------------------------------------------------------------- 1 | # Smart Contract Data for Analysis 2 | 3 | See the notes on [academic usage](academia.md) for information on how 4 | to cite the datasets. 5 | 6 | - 10 contracts: The folder 7 | [`samples`](https://github.com/smartbugs/smartbugs/tree/master/samples) 8 | contains a few selected Solidity source files with the corresponding 9 | deployment and runtime bytecodes, to test the installation. 10 | 11 | - 143 contracts: [SB 12 | Curated](https://github.com/smartbugs/smartbugs-curated) is a 13 | curated dataset of vulnerable Solidity smart contracts. 14 | 15 | - 3103/2529/2473 contracts as source/deployment/runtime code: 16 | [Consolidated Ground Truth (CGT)](https://github.com/gsalzer/cgt) 17 | is a unified and consolidated ground truth with 20,455 manually 18 | checked assessments (positive and negative) of security-related 19 | properties. 20 | 21 | - 47,398 contracts: [SmartBugs Wild 22 | Dataset](https://github.com/smartbugs/smartbugs-wild) is a 23 | repository with smart contracts extracted from the Ethereum 24 | network. 25 | 26 | - 248,328 contracts: [Skelcodes](https://github.com/gsalzer/skelcodes) 27 | is a repository of deployment and runtime codes, with an indication 28 | if the source code is available on Etherscan. By the way the 29 | contracts were selected, they faithfully represent, in most 30 | respects, the 45 million contracts successfully deployed up to block 31 | 14,000,000. 32 | -------------------------------------------------------------------------------- /doc/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Requirements 4 | 5 | - Linux, MacOS or Windows 6 | - [Docker](https://docs.docker.com/install) 7 | - [Python3](https://www.python.org) (version 3.6 and above, 3.10+ recommended) 8 | 9 | ## Linux/MacOS/Unix 10 | 11 | 1. Install [Docker](https://docs.docker.com/install) and [Python3](https://www.python.org). 12 | 13 | Make sure that the user running SmartBugs has permission to interact with the Docker daemon, by adding the user to the `docker` group: 14 | 15 | ```bash 16 | sudo usermod -a -G docker $USER 17 | ``` 18 | 19 | For adding someone else, replace `$USER` by the respective 20 | user-id. The group membership becomes active with the next log-in. 21 | 22 | 2. Clone [SmartBugs's repository](https://github.com/smartbugs/smartbugs): 23 | 24 | ```bash 25 | git clone https://github.com/smartbugs/smartbugs 26 | ``` 27 | 28 | 3. Install Python dependencies in a virtual environment: 29 | 30 | ```bash 31 | cd smartbugs 32 | install/setup-venv.sh 33 | ``` 34 | 35 | 4. Optionally, add the executables to the command search path, e.g. by adding links to `$HOME/bin`. 36 | 37 | ```bash 38 | ln -s "`pwd`/smartbugs" "$HOME/bin/smartbugs" 39 | ln -s "`pwd`/reparse" "$HOME/bin/reparse" 40 | ln -s "`pwd`/results2csv" "$HOME/bin/results2csv" 41 | ``` 42 | 43 | The command `which smartbugs` should now display the path to the command. 44 | 45 | 46 | ## Windows 47 | 48 | See [our wiki page on running SmartBugs in Windows](https://github.com/smartbugs/smartbugs/wiki/Running-SmartBugs-in-Windows). 49 | 50 | -------------------------------------------------------------------------------- /doc/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | SmartBugs provides a command-line interface. Run it without arguments for a short description. 4 | For details, see [SmartBugs' wiki](https://github.com/smartbugs/smartbugs/wiki/The-command-line-interface). 5 | 6 | 7 | ```console 8 | ./smartbugs 9 | usage: smartbugs [-c FILE] [-t TOOL [TOOL ...]] [-f PATTERN [PATTERN ...]] [--main] [--runtime] 10 | [--processes N] [--timeout N] [--cpu-quota N] [--mem-limit MEM] 11 | [--runid ID] [--results DIR] [--log FILE] [--overwrite] [--json] [--sarif] [--quiet] 12 | [--version] [-h] 13 | ... 14 | ``` 15 | 16 | **Example:** To analyse the Solidity files in the `samples` directory with Mythril, use the command 17 | 18 | ```console 19 | ./smartbugs -t mythril -f samples/*.sol --processes 2 --mem-limit 4g --timeout 600 20 | ``` 21 | 22 | The options tell SmartBugs to run two processes in parallel, with a memory limit of 4GB and max. 10 minutes computation time per task. 23 | By default, the results are placed in the local directory `results`. 24 | 25 | ## Utility programs 26 | 27 | **`reparse`** can be used to parse analysis results and extract relevant information, without rerunning the analysis. 28 | This may be useful either when you did not specify the option `--json` or `--sarif` during analysis, or when you want to parse old analysis results with an updated parser. 29 | 30 | ```console 31 | ./reparse 32 | usage: reparse [-h] [--sarif] [--processes N] [-v] DIR [DIR ...] 33 | ``` 34 | 35 | **`results2csv`** generates a csv file from the results, suitable e.g. for a database. 36 | 37 | ```console 38 | ./results2csv 39 | usage: results2csv [-h] [-p] [-v] [-f FIELD [FIELD ...]] [-x FIELD [FIELD ...]] DIR [DIR ...] 40 | ``` 41 | 42 | The following commands analyse `SimpleDAO.sol` with all available tools and write the parsed output to `results.csv`. 43 | `reparse` is necessary in this example, since `smartbugs` is called without the options `--json` and `--sarif`, so SmartBugs doesn't parse during the analysis. 44 | `results2csv` collects the outputs in the folder `results` and writes for each analysed contract one line of comma-separated values to standard output (redirected to `results.csv`). 45 | The option `-p` tells `results2csv` to format the lists of findings, errors etc. as Postgres arrays; without the option, the csv file is suitable for spreadsheet programs. 46 | 47 | ```console 48 | ./smartbugs -t all -f samples/SimpleDAO.sol 49 | ./reparse results 50 | ./results2csv -p results > results.csv 51 | ``` 52 | -------------------------------------------------------------------------------- /install/docker_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import docker 4 | 5 | print("get handle to communicate with docker") 6 | client = docker.from_env() 7 | 8 | print("testing the communication") 9 | client.info() 10 | 11 | print("load a sample image") 12 | image = "hello-world" 13 | client.images.pull(image) 14 | 15 | image_list = client.images.list(image) 16 | if image_list: 17 | print(f"image {image} has been loaded") 18 | else: 19 | print(f"loading of {image} failed") 20 | 21 | print("run the container, retrieve the output, and print the second line") 22 | container = client.containers.run(image="hello-world", detach=True) 23 | logs = container.logs().decode("utf8").splitlines() 24 | print(f"*** {logs[1]} ***") 25 | 26 | print("cleaning up") 27 | container.stop() 28 | try: 29 | container.kill() 30 | print("container shouldn't have been running anymore") 31 | except: 32 | pass 33 | container.remove() 34 | -------------------------------------------------------------------------------- /install/requirements-3.10.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.1.1 3 | colorama==0.4.6 4 | docker==6.0.1 5 | idna==3.7 6 | packaging==21.3 7 | py-cpuinfo==9.0.0 8 | pyparsing==3.0.9 9 | PyYAML==6.0 10 | requests==2.32.0 11 | semantic-version==2.10.0 12 | urllib3==1.26.19 13 | websocket-client==1.4.2 14 | -------------------------------------------------------------------------------- /install/requirements-3.11.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.1.1 3 | colorama==0.4.6 4 | docker==6.0.1 5 | idna==3.7 6 | packaging==21.3 7 | py-cpuinfo==9.0.0 8 | pyparsing==3.0.9 9 | PyYAML==6.0 10 | requests==2.32.0 11 | semantic-version==2.10.0 12 | urllib3==1.26.19 13 | websocket-client==1.4.2 14 | -------------------------------------------------------------------------------- /install/requirements-3.12.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==3.3.2 3 | colorama==0.4.6 4 | docker==7.1.0 5 | idna==3.7 6 | py-cpuinfo==9.0.0 7 | PyYAML==6.0.1 8 | requests==2.32.3 9 | semantic-version==2.10.0 10 | urllib3==2.2.2 11 | wheel==0.43.0 12 | -------------------------------------------------------------------------------- /install/requirements-3.6.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.0.12 3 | colorama==0.4.5 4 | docker==5.0.3 5 | idna==3.7 6 | py-cpuinfo==9.0.0 7 | PyYAML==6.0 8 | requests==2.32.0 9 | semantic-version==2.10.0 10 | urllib3==1.26.19 11 | websocket-client==1.3.1 12 | -------------------------------------------------------------------------------- /install/requirements-3.7.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.1.1 3 | colorama==0.4.6 4 | docker==6.0.1 5 | idna==3.7 6 | packaging==21.3 7 | py-cpuinfo==9.0.0 8 | pyparsing==3.0.9 9 | PyYAML==6.0 10 | requests==2.32.0 11 | semantic-version==2.10.0 12 | urllib3==1.26.19 13 | websocket-client==1.4.2 14 | -------------------------------------------------------------------------------- /install/requirements-3.8.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.1.1 3 | colorama==0.4.6 4 | docker==6.0.1 5 | idna==3.7 6 | packaging==21.3 7 | py-cpuinfo==9.0.0 8 | pyparsing==3.0.9 9 | PyYAML==6.0 10 | requests==2.32.0 11 | semantic-version==2.10.0 12 | urllib3==1.26.19 13 | websocket-client==1.4.2 14 | -------------------------------------------------------------------------------- /install/requirements-3.9.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.7.4 2 | charset-normalizer==2.1.1 3 | colorama==0.4.6 4 | docker==6.0.1 5 | idna==3.7 6 | packaging==21.3 7 | py-cpuinfo==9.0.0 8 | pyparsing==3.0.9 9 | PyYAML==6.0 10 | requests==2.32.0 11 | semantic-version==2.10.0 12 | urllib3==1.26.19 13 | websocket-client==1.4.2 14 | -------------------------------------------------------------------------------- /install/setup-venv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # tested for python >= 3.6.9 4 | # python < 3.10 will give an error when using the ':'-feature in input patterns 5 | python3 -m venv venv 6 | source venv/bin/activate 7 | 8 | # avoid spurious errors/warnings; the next two lines could be omitted 9 | pip install --upgrade pip 10 | pip install wheel 11 | 12 | # install the packages needed by smartbugs 13 | pip install pyyaml colorama requests semantic_version docker py-cpuinfo 14 | -------------------------------------------------------------------------------- /reparse: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # determine SmartBugs' home directory, from the location of this script 4 | SOURCE=${BASH_SOURCE[0]} 5 | while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 6 | DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 7 | SOURCE=$(readlink "$SOURCE") 8 | [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 9 | done 10 | SB=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 11 | 12 | source "$SB/venv/bin/activate" 13 | PYTHONPATH="$SB:$PYTHONPATH" python -m sb.reparse $* 14 | -------------------------------------------------------------------------------- /results2csv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # determine SmartBugs' home directory, from the location of this script 4 | SOURCE=${BASH_SOURCE[0]} 5 | while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 6 | DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 7 | SOURCE=$(readlink "$SOURCE") 8 | [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 9 | done 10 | SB=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 11 | 12 | source "$SB/venv/bin/activate" 13 | PYTHONPATH="$SB:$PYTHONPATH" python -m sb.results2csv $* 14 | 15 | -------------------------------------------------------------------------------- /samples/EtherLotto.hex: -------------------------------------------------------------------------------- 1 | 6060604052341561000f57600080fd5b5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b610235806100616000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ba2363a1461005457806376cdb03b1461007d57806393e84cd9146100d2575b600080fd5b341561005f57600080fd5b6100676100dc565b6040518082815260200191505060405180910390f35b341561008857600080fd5b6100906100e2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100da610107565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600a3414151561011557fe5b346001600082825401925050819055506002426040518082815260200191505060405180910390206001900481151561014a57fe5b0690506000811415610205576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc60019081150290604051600060405180830381858888f1935050505015156101b857600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc60018054039081150290604051600060405180830381858888f1935050505015156101fc57600080fd5b60006001819055505b5b505600a165627a7a7230582021347d94582729878eb74356b7b81bc6ccc1b141e4f978349ae1e9166c1f8cd90029 2 | -------------------------------------------------------------------------------- /samples/EtherLotto.rt.hex: -------------------------------------------------------------------------------- 1 | 60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ba2363a1461005457806376cdb03b1461007d57806393e84cd9146100d2575b600080fd5b341561005f57600080fd5b6100676100dc565b6040518082815260200191505060405180910390f35b341561008857600080fd5b6100906100e2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100da610107565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600a3414151561011557fe5b346001600082825401925050819055506002426040518082815260200191505060405180910390206001900481151561014a57fe5b0690506000811415610205576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc60019081150290604051600060405180830381858888f1935050505015156101b857600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc60018054039081150290604051600060405180830381858888f1935050505015156101fc57600080fd5b60006001819055505b5b505600a165627a7a7230582021347d94582729878eb74356b7b81bc6ccc1b141e4f978349ae1e9166c1f8cd90029 2 | -------------------------------------------------------------------------------- /samples/EtherLotto.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * @article: https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620 3 | * @source: https://etherscan.io/address/0xa11e4ed59dc94e69612f3111942626ed513cb172#code 4 | * @vulnerable_at_lines: 43 5 | * @author: - 6 | */ 7 | 8 | pragma solidity ^0.4.15; 9 | 10 | /// @title Ethereum Lottery Game. 11 | 12 | contract EtherLotto { 13 | 14 | // Amount of ether needed for participating in the lottery. 15 | uint constant TICKET_AMOUNT = 10; 16 | 17 | // Fixed amount fee for each lottery game. 18 | uint constant FEE_AMOUNT = 1; 19 | 20 | // Address where fee is sent. 21 | address public bank; 22 | 23 | // Public jackpot that each participant can win (minus fee). 24 | uint public pot; 25 | 26 | // Lottery constructor sets bank account from the smart-contract owner. 27 | function EtherLotto() { 28 | bank = msg.sender; 29 | } 30 | 31 | // Public function for playing lottery. Each time this function 32 | // is invoked, the sender has an oportunity for winning pot. 33 | function play() payable { 34 | 35 | // Participants must spend some fixed ether before playing lottery. 36 | assert(msg.value == TICKET_AMOUNT); 37 | 38 | // Increase pot for each participant. 39 | pot += msg.value; 40 | 41 | // Compute some *almost random* value for selecting winner from current transaction. 42 | // TIME_MANIPULATION 43 | var random = uint(sha3(block.timestamp)) % 2; 44 | 45 | // Distribution: 50% of participants will be winners. 46 | if (random == 0) { 47 | 48 | // Send fee to bank account. 49 | bank.transfer(FEE_AMOUNT); 50 | 51 | // Send jackpot to winner. 52 | msg.sender.transfer(pot - FEE_AMOUNT); 53 | 54 | // Restart jackpot. 55 | pot = 0; 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /samples/MyToken.hex: -------------------------------------------------------------------------------- 1 | 6060604052341561000c57fe5b5b612710600060003273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5b6102b9806100646000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806390b98a1114610046578063f8b2cb4f1461009d575bfe5b341561004e57fe5b610083600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506100e7565b604051808215151515815260200191505060405180910390f35b34156100a557fe5b6100d1600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610243565b6040518082815260200191505060405180910390f35b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610139576000905061023d565b81600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a72305820e02668a27c93cf12d556b35e325871dcb26c648dbb2fc1498783adbe0a30b5d40029 2 | -------------------------------------------------------------------------------- /samples/MyToken.rt.hex: -------------------------------------------------------------------------------- 1 | 60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806390b98a1114610046578063f8b2cb4f1461009d575bfe5b341561004e57fe5b610083600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506100e7565b604051808215151515815260200191505060405180910390f35b34156100a557fe5b6100d1600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610243565b6040518082815260200191505060405180910390f35b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610139576000905061023d565b81600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a72305820e02668a27c93cf12d556b35e325871dcb26c648dbb2fc1498783adbe0a30b5d40029 2 | -------------------------------------------------------------------------------- /samples/MyToken.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * @source: https://ericrafaloff.com/analyzing-the-erc20-short-address-attack/ 3 | * @author: - 4 | * @vulnerable_at_lines: 18 5 | */ 6 | 7 | pragma solidity ^0.4.11; 8 | 9 | contract MyToken { 10 | mapping (address => uint) balances; 11 | 12 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 13 | 14 | function MyToken() { 15 | balances[tx.origin] = 10000; 16 | } 17 | // SHORT_ADDRESSES 18 | function sendCoin(address to, uint amount) returns(bool sufficient) { 19 | if (balances[msg.sender] < amount) return false; 20 | balances[msg.sender] -= amount; 21 | balances[to] += amount; 22 | Transfer(msg.sender, to, amount); 23 | return true; 24 | } 25 | 26 | function getBalance(address addr) constant returns(uint) { 27 | return balances[addr]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /samples/OpenAddressLottery.rt.hex: -------------------------------------------------------------------------------- 1 | 606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806337354a68146100d757806341c0e1b51461012457806380ca7aec14610139578063d11711a21461014e575b67016345785d8a000034101580156100c757506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b156100d5576100d4610158565b5b005b34156100e257600080fd5b61010e600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061030c565b6040518082815260200191505060405180910390f35b341561012f57600080fd5b6101376103ab565b005b341561014457600080fd5b61014c61041f565b005b610156610158565b005b600067016345785d8a000034101561016f57610309565b60001515600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151415156101ce57600080fd5b6003546101da3361030c565b14156102b9576001600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506007340290503073ffffffffffffffffffffffffffffffffffffffff1631811115610278573073ffffffffffffffffffffffffffffffffffffffff163190505b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015156102b857600080fd5b5b6103e860025443031115610308576103076080604051908101604052804173ffffffffffffffffffffffffffffffffffffffff16815260200144815260200145815260200142815250610519565b5b5b50565b600060088273ffffffffffffffffffffffffffffffffffffffff1660015460405180838152602001828152602001925050506040518091039020600060208110151561035457fe5b1a7f0100000000000000000000000000000000000000000000000000000000000000027f010000000000000000000000000000000000000000000000000000000000000090048115156103a357fe5b069050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561040657600080fd5b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561047c57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff16816000018190555060014303406001900481600101819055504173ffffffffffffffffffffffffffffffffffffffff164402816002018190555060073a0281600301819055506105168160806040519081016040529081600082015481526020016001820154815260200160028201548152602001600382015481525050610519565b50565b80600001518160200151826040015183606001516040518085815260200184815260200183815260200182815260200194505050505060405180910390206001900460018190555043600281905550505600a165627a7a723058206b596e01d5865f77a048963f5552069d7c81e298834762b54349dc70ffffe0d00029 2 | -------------------------------------------------------------------------------- /samples/ReturnValue.hex: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b5061016b806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637536185e14610051578063bf9bd6cb14610094575b600080fd5b34801561005d57600080fd5b50610092600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100d7565b005b3480156100a057600080fd5b506100d5600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610110565b005b8073ffffffffffffffffffffffffffffffffffffffff166040516000604051808303816000865af1915050151561010d57600080fd5b50565b8073ffffffffffffffffffffffffffffffffffffffff166040516000604051808303816000865af191505050505600a165627a7a723058200cdaacb44cab93f6b1975b15afb357b8476219fff17c7525f7170543601df1200029 2 | -------------------------------------------------------------------------------- /samples/ReturnValue.rt.hex: -------------------------------------------------------------------------------- 1 | 60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637536185e14610051578063bf9bd6cb14610094575b600080fd5b34801561005d57600080fd5b50610092600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100d7565b005b3480156100a057600080fd5b506100d5600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610110565b005b8073ffffffffffffffffffffffffffffffffffffffff166040516000604051808303816000865af1915050151561010d57600080fd5b50565b8073ffffffffffffffffffffffffffffffffffffffff166040516000604051808303816000865af191505050505600a165627a7a723058200cdaacb44cab93f6b1975b15afb357b8476219fff17c7525f7170543601df1200029 2 | -------------------------------------------------------------------------------- /samples/ReturnValue.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * @source: https://smartcontractsecurity.github.io/SWC-registry/docs/SWC-104#unchecked-return-valuesol 3 | * @author: - 4 | * @vulnerable_at_lines: 17 5 | */ 6 | 7 | pragma solidity 0.4.25; 8 | 9 | contract ReturnValue { 10 | 11 | function callchecked(address callee) public { 12 | require(callee.call()); 13 | } 14 | 15 | function callnotchecked(address callee) public { 16 | // UNCHECKED_LL_CALLS 17 | callee.call(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/SimpleDAO.hex: -------------------------------------------------------------------------------- 1 | 6060604052341561000c57fe5b5b6102ee8061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168062362a951461005b5780632e1a7d4d1461008957806359f1286d146100a9578063d5d44d80146100f3575bfe5b610087600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061013d565b005b341561009157fe5b6100a7600480803590602001909190505061018e565b005b34156100b157fe5b6100dd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610260565b6040518082815260200191505060405180910390f35b34156100fb57fe5b610127600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102aa565b6040518082815260200191505060405180910390f35b34600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b50565b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151561025b573373ffffffffffffffffffffffffffffffffffffffff168260405180905060006040518083038185876187965a03f192505050905081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b5b5050565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b600060205280600052604060002060009150905054815600a165627a7a723058202c73b3d47e191d62bcac27bf7b694918d1abffa8f67daed91791bbd8a94170710029 2 | -------------------------------------------------------------------------------- /samples/SimpleDAO.rt.hex: -------------------------------------------------------------------------------- 1 | 60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168062362a951461005b5780632e1a7d4d1461008957806359f1286d146100a9578063d5d44d80146100f3575bfe5b610087600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061013d565b005b341561009157fe5b6100a7600480803590602001909190505061018e565b005b34156100b157fe5b6100dd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610260565b6040518082815260200191505060405180910390f35b34156100fb57fe5b610127600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102aa565b6040518082815260200191505060405180910390f35b34600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b50565b600081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151561025b573373ffffffffffffffffffffffffffffffffffffffff168260405180905060006040518083038185876187965a03f192505050905081600060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b5b5050565b6000600060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b600060205280600052604060002060009150905054815600a165627a7a723058202c73b3d47e191d62bcac27bf7b694918d1abffa8f67daed91791bbd8a94170710029 2 | -------------------------------------------------------------------------------- /samples/SimpleDAO.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * @source: http://blockchain.unica.it/projects/ethereum-survey/attacks.html#simpledao 3 | * @author: - 4 | * @vulnerable_at_lines: 19 5 | */ 6 | 7 | pragma solidity ^0.4.2; 8 | 9 | contract SimpleDAO { 10 | mapping (address => uint) public credit; 11 | 12 | function donate(address to) payable { 13 | credit[to] += msg.value; 14 | } 15 | 16 | function withdraw(uint amount) { 17 | if (credit[msg.sender]>= amount) { 18 | // REENTRANCY 19 | bool res = msg.sender.call.value(amount)(); 20 | credit[msg.sender]-=amount; 21 | } 22 | } 23 | 24 | function queryCredit(address to) returns (uint){ 25 | return credit[to]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cboy220/smartbugs/c37c9bec49913e75571c3c15afeb2f007c73a186/sb/__init__.py -------------------------------------------------------------------------------- /sb/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """SmartBugs: A framework to analyze smart contracts 3 | 4 | http://github.com/smartbugs/smartbugs 5 | """ 6 | 7 | import sb.cli 8 | 9 | if __name__ == "__main__": 10 | sb.cli.main() 11 | -------------------------------------------------------------------------------- /sb/cfg.py: -------------------------------------------------------------------------------- 1 | import os, time, cpuinfo, platform 2 | 3 | VERSION = "2.0.10" 4 | HOME = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 5 | SITE_CFG = os.path.join(HOME,"site_cfg.yaml") 6 | TASK_LOG = "smartbugs.json" 7 | TOOLS_HOME = os.path.join(HOME,"tools") 8 | TOOL_CONFIG = "config.yaml" 9 | TOOL_FINDINGS = "findings.yaml" 10 | TOOL_PARSER = "parser.py" 11 | TOOL_LOG = "result.log" 12 | TOOL_OUTPUT = "result.tar" 13 | PARSER_OUTPUT = "result.json" 14 | SARIF_OUTPUT = "result.sarif" 15 | 16 | CPU = cpuinfo.get_cpu_info() 17 | UNAME = platform.uname() 18 | PLATFORM = { 19 | "smartbugs": VERSION, 20 | "python": CPU.get("python_version"), 21 | "system": UNAME.system, 22 | "release": UNAME.release, 23 | "version": UNAME.version, 24 | "cpu": CPU.get("brand_raw"), 25 | } 26 | 27 | DEBUG = False 28 | -------------------------------------------------------------------------------- /sb/colors.py: -------------------------------------------------------------------------------- 1 | import colorama, re, sys 2 | from colorama import Fore, Style 3 | 4 | ANSIcolor = re.compile("\x1b\\[[^m]*m") 5 | def strip(s): 6 | return ANSIcolor.sub('',str(s)) 7 | 8 | if sys.platform == "win32": 9 | def color(col, s): 10 | return s 11 | else: 12 | def color(col, s): 13 | return f"{col}{s}{Style.RESET_ALL}" 14 | 15 | def file(s): 16 | return color(Fore.BLUE, s) 17 | 18 | def tool(s): 19 | return color(Fore.CYAN, s) 20 | 21 | def error(s): 22 | return color(Fore.RED, s) 23 | 24 | def warning(s): 25 | return color(Fore.YELLOW, s) 26 | 27 | def success(s): 28 | return color(Fore.GREEN, s) 29 | 30 | -------------------------------------------------------------------------------- /sb/errors.py: -------------------------------------------------------------------------------- 1 | class InternalError(Exception): 2 | pass 3 | 4 | class SmartBugsError(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /sb/io.py: -------------------------------------------------------------------------------- 1 | import yaml, json 2 | import sb.errors 3 | 4 | def read_yaml(fn): 5 | try: 6 | with open(fn, 'r', encoding='utf-8') as f: 7 | # for an empty file, return empty dict, not NoneType 8 | return yaml.safe_load(f) or {} 9 | except Exception as e: 10 | raise sb.errors.SmartBugsError(e) 11 | 12 | def read_json(fn): 13 | try: 14 | with open(fn, 'r', encoding='utf-8') as f: 15 | return json.load(f) 16 | except Exception as e: 17 | raise sb.errors.SmartBugsError(e) 18 | 19 | def write_json(fn, output): 20 | try: 21 | j = json.dumps(output, sort_keys=True, indent=4) 22 | with open(fn, 'w', encoding='utf-8') as f: 23 | print(j, file=f) 24 | except Exception as e: 25 | raise sb.errors.SmartBugsError(e) 26 | 27 | def read_lines(fn): 28 | try: 29 | with open(fn, 'r', encoding='utf-8') as f: 30 | return f.read().splitlines() 31 | except Exception as e: 32 | raise sb.errors.SmartBugsError(e) 33 | 34 | def write_txt(fn, output): 35 | try: 36 | with open(fn, 'w', encoding='utf-8') as f: 37 | if isinstance(output, str): 38 | f.write(output) 39 | else: 40 | for line in output: 41 | f.write(f"{line}\n") 42 | except Exception as e: 43 | raise sb.errors.SmartBugsError(e) 44 | 45 | def read_bin(fn): 46 | try: 47 | with open(fn, 'rb') as f: 48 | return f.read() 49 | except Exception as e: 50 | raise sb.errors.SmartBugsError(e) 51 | 52 | def write_bin(fn, output): 53 | try: 54 | with open(fn, 'wb') as f: 55 | f.write(output) 56 | except Exception as e: 57 | raise sb.errors.SmartBugsError(e) 58 | 59 | -------------------------------------------------------------------------------- /sb/logging.py: -------------------------------------------------------------------------------- 1 | import multiprocessing, threading, os, sys, time, re 2 | import sb.colors 3 | 4 | def logger_process(logfn, overwrite, queue, prolog): 5 | log_parent_folder = os.path.dirname(logfn) 6 | if log_parent_folder: 7 | os.makedirs(log_parent_folder, exist_ok=True) 8 | mode = "w" if overwrite else "a" 9 | with open(logfn, mode) as logfile: 10 | for log in prolog: 11 | print(log, file=logfile) 12 | while True: 13 | log = queue.get() 14 | if log is None: 15 | break 16 | print(log, file=logfile) 17 | 18 | __prolog = [] 19 | 20 | def start(logfn, append, queue): 21 | global logger 22 | logger = threading.Thread(target=logger_process, args=(logfn,append,queue,__prolog)) 23 | logger.start() 24 | 25 | quiet = False 26 | 27 | def message(con=None, log=None, queue=None): 28 | if con and log=="": 29 | log = sb.colors.strip(con) 30 | if con and not quiet: 31 | print(con, flush=True) 32 | if log: 33 | if queue: 34 | queue.put(log) 35 | else: 36 | __prolog.append(log) 37 | 38 | def stop(queue): 39 | queue.put(None) 40 | logger.join() 41 | 42 | -------------------------------------------------------------------------------- /sb/parse_utils.py: -------------------------------------------------------------------------------- 1 | '''Utilities for the output parsers''' 2 | 3 | import re 4 | 5 | DOCKER_CODES = { 6 | 125: "DOCKER_INVOCATION_PROBLEM", 7 | 126: "DOCKER_CMD_NOT_EXECUTABLE", 8 | 127: "DOCKER_CMD_NOT_FOUND", 9 | 137: "DOCKER_KILL_OOM", # container received KILL signal, manually or because out of memory 10 | 139: "DOCKER_SEGV", # segmentation violation 11 | 143: "DOCKER_TERM" # container was externally stopped 12 | } 13 | 14 | 15 | ANSI = re.compile("\x1b\\[[^m]*m") 16 | def discard_ANSI(lines): 17 | return ( ANSI.sub('',line) for line in lines ) 18 | 19 | 20 | def truncate_message(m, length=205): 21 | half_length = (length-5)//2 22 | return m if len(m) <= length else m[:half_length]+' ... '+m[-half_length:] 23 | 24 | 25 | TRACEBACK = "Traceback (most recent call last):" # Python 26 | 27 | EXCEPTIONS = ( 28 | re.compile(".*line [0-9: ]*(Segmentation fault|Killed)"), # Shell 29 | re.compile('Exception in thread "[^"]*" (.*)'), # Java 30 | re.compile("thread '[^']*' panicked at '([^']*)'"), # Rust 31 | ) 32 | 33 | def exceptions(lines): 34 | exceptions = set() 35 | traceback = False 36 | for line in lines: 37 | if traceback: 38 | if line and line[0] != " ": 39 | exceptions.add(f"exception ({line})") 40 | traceback = False 41 | elif line.endswith(TRACEBACK): 42 | traceback = True 43 | else: 44 | for re_exception in EXCEPTIONS: 45 | m = re_exception.match(line) 46 | if m: 47 | exceptions.add(f"exception ({m[1]})") 48 | return exceptions 49 | 50 | 51 | def add_match(matches, line, patterns): 52 | for pattern in patterns: 53 | m = pattern.match(line) 54 | if m: 55 | matches.add(m[1]) 56 | return True 57 | return False 58 | 59 | 60 | def errors_fails(exit_code, log, log_expected=True): 61 | errors = set() # errors detected and handled by the tool 62 | fails = set() # exceptions not caught by the tool, or outside events leading to abortion 63 | if exit_code is None: 64 | fails.add('DOCKER_TIMEOUT') 65 | elif exit_code == 0: 66 | pass 67 | elif exit_code == 127: 68 | fails.add("SmartBugs was invoked with option 'main', but the filename did not match any contract") 69 | elif exit_code in DOCKER_CODES: 70 | fails.add(DOCKER_CODES[exit_code]) 71 | elif 128 <= exit_code <= 128+64: 72 | fails.add(f"DOCKER_RECEIVED_SIGNAL_{exit_code-128}") 73 | else: 74 | # remove it for individual signals and tools, where it is not an error 75 | errors.add(f"EXIT_CODE_{exit_code}") 76 | if log: 77 | fails.update(exceptions(log)) 78 | elif log_expected and not fails: 79 | fails.add('execution failed') 80 | return errors, fails 81 | 82 | -------------------------------------------------------------------------------- /sb/parsing.py: -------------------------------------------------------------------------------- 1 | import os, importlib.util 2 | import sb.cfg, sb.errors 3 | 4 | tool_parsers = {} 5 | 6 | def get_parser(tool): 7 | tid,tmode = tool["id"],tool["mode"] 8 | key = (tid,tmode) 9 | if key not in tool_parsers: 10 | try: 11 | modulename = f"tools.{tid}.{tmode}" 12 | fn = os.path.join(sb.cfg.TOOLS_HOME, tid, tool["parser"]) 13 | spec = importlib.util.spec_from_file_location(modulename, fn) 14 | module = importlib.util.module_from_spec(spec) 15 | spec.loader.exec_module(module) 16 | tool_parsers[key] = module 17 | except Exception as e: 18 | raise sb.errors.SmartBugsError(f"Cannot load parser for {tid}/{tmode}\n{e}") 19 | return tool_parsers[key] 20 | 21 | 22 | 23 | def parse(task_log, tool_log, tool_output): 24 | tool = task_log["tool"] 25 | filename = task_log["filename"] 26 | exit_code = task_log["result"]["exit_code"] 27 | 28 | tool_parser = get_parser(tool) 29 | try: 30 | findings,infos,errors,fails = tool_parser.parse(exit_code, tool_log, tool_output) 31 | for finding in findings: 32 | # if FINDINGS is defined, ensure that the current finding is in FINDINGS 33 | # irrelevant for SmartBugs, but may be relevant for programs further down the line 34 | if tool_parser.FINDINGS and finding["name"] not in tool_parser.FINDINGS: 35 | raise sb.errors.SmartBugsError(f"'{finding['name']}' not among the findings of {tool['id']}") 36 | # check that filename within docker corresponds to filename outside, before replacing it 37 | # splitting at "/" is ok, since it is a Linux path from within the docker container 38 | assert not finding.get("filename") or filename.endswith(finding["filename"].split("/")[-1]) 39 | finding["filename"] = filename 40 | except Exception as e: 41 | raise 42 | # raise sb.errors.SmartBugsError(f"Parsing of results failed\n{e}") 43 | 44 | 45 | return { 46 | "findings": findings, 47 | "infos": sorted(infos), 48 | "errors": sorted(errors), 49 | "fails": sorted(fails), 50 | "parser": { 51 | "id": tool["id"], 52 | "mode": tool["mode"], 53 | "version": tool_parser.VERSION 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /sb/solidity.py: -------------------------------------------------------------------------------- 1 | import os,re 2 | from pathlib import Path 3 | 4 | import solcx 5 | # load binaries for Linux in Docker images, not for host platform 6 | solcx.set_target_os("linux") 7 | 8 | 9 | 10 | VOID_START = re.compile("//|/\\*|\"|'") 11 | QUOTE_END = re.compile("(?=0.y.z to ^0.y.z 75 | pragma = re.sub(r">=0\.", r"^0.", pragma) 76 | # replace x.y by x.y.0 77 | pragma = re.sub(r"([^0-9])([0-9]+\.[0-9]+)([^0-9.]|$)", r"\1\2.0\3", pragma) 78 | try: 79 | version = solcx.install._select_pragma_version(pragma, cached_solc_versions) 80 | except Exception: 81 | version = None 82 | return version 83 | 84 | 85 | 86 | cached_solc_paths = {} 87 | 88 | def get_solc_path(version): 89 | if not version: 90 | return None 91 | if version in cached_solc_paths: 92 | return cached_solc_paths[version] 93 | try: 94 | solcx.install_solc(version) 95 | solc_path = solcx.get_executable(version) 96 | except Exception: 97 | solc_path = None 98 | cached_solc_paths[version] = solc_path 99 | return solc_path 100 | -------------------------------------------------------------------------------- /sb/tasks.py: -------------------------------------------------------------------------------- 1 | class Task: 2 | def __init__(self, absfn, relfn, rdir, solc_version, solc_path, tool, settings): 3 | self.absfn = absfn # absolute normalized path 4 | self.relfn = relfn # path within project 5 | self.rdir = rdir # directory for results 6 | self.solc_version = solc_version 7 | self.solc_path = solc_path 8 | self.tool = tool 9 | self.settings = settings 10 | 11 | def __str__(self): 12 | s = [ f"{k}: {str(v)}" for k,v in self.__dict__.items() ] 13 | return f"{{{', '.join(s)}}}" 14 | -------------------------------------------------------------------------------- /sb/utils.py: -------------------------------------------------------------------------------- 1 | def str2label(s): 2 | """Convert string to label. 3 | 4 | - leading non-letters are removed 5 | - trailing characters that are neither letters nor digits ("other chars") are removed 6 | - sequences of other chars within the string are replaced by a single underscore 7 | """ 8 | l = "" 9 | separator = False 10 | has_started = False 11 | for c in s: 12 | if c.isalpha() or (has_started and c.isdigit()): 13 | has_started = True 14 | if separator: 15 | separator = False 16 | l += "_" 17 | l += c 18 | else: 19 | separator = has_started 20 | return l 21 | -------------------------------------------------------------------------------- /site_cfg.yaml: -------------------------------------------------------------------------------- 1 | #files: [] 2 | ## $HOME or ${HOME} is replaced by the home dir of the current user 3 | # 4 | #runtime: false 5 | # 6 | #main: false 7 | # 8 | #tools: [] 9 | # 10 | #runid: ${YEAR}${MONTH}${DAY}_${HOUR}${MIN} 11 | ## vars: YEAR, MONTH, DAY, HOUR, MIN, SEC, ZONE, 12 | ## HOME, PID, SBVERSION, SBHOME 13 | # 14 | #overwrite: false 15 | # 16 | #processes: 1 17 | # 18 | #timeout: 0 # [s] 0/null = no timeout enforced, tool default applies 19 | # 20 | #cpu-quota: 0 # 0/null = no quota 21 | # 22 | #mem-limit: 0 # "512m" or "4g" 0/null = no quota 23 | # 24 | #results: results/${TOOL}/${RUNID}/${FILENAME} 25 | ## vars: all vars from "runid" above, as well as RUNID, 26 | ## TOOL, MODE (solidity, bytecode, runtime), ABSDIR, RELDIR, 27 | ## FILENAME, FILEBASE, FILEEXT (FILENAME = FILEBASE + "." + FILEEXT) 28 | # 29 | #log: results/logs/${RUNID}.log 30 | ## vars: all vars from "runid" above, as well as RUNID 31 | # 32 | #json: false 33 | # 34 | #sarif: false 35 | # 36 | #quiet: false 37 | -------------------------------------------------------------------------------- /smartbugs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # determine SmartBugs' home directory, from the location of this script 4 | SOURCE=${BASH_SOURCE[0]} 5 | while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 6 | DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 7 | SOURCE=$(readlink "$SOURCE") 8 | [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 9 | done 10 | SB=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) 11 | 12 | source "$SB/venv/bin/activate" 13 | PYTHONPATH="$SB:$PYTHONPATH" python -m sb "$@" 14 | -------------------------------------------------------------------------------- /solcx/__init__.py: -------------------------------------------------------------------------------- 1 | from solcx.install import ( 2 | compile_solc, 3 | get_compilable_solc_versions, 4 | get_executable, 5 | get_installable_solc_versions, 6 | get_installed_solc_versions, 7 | get_solcx_install_folder, 8 | import_installed_solc, 9 | install_solc, 10 | install_solc_pragma, 11 | set_target_os, 12 | set_solc_version, 13 | set_solc_version_pragma, 14 | ) 15 | from solcx.main import ( 16 | compile_files, 17 | compile_source, 18 | compile_standard, 19 | get_solc_version, 20 | link_code 21 | ) 22 | -------------------------------------------------------------------------------- /solcx/exceptions.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List 2 | 3 | 4 | class SolcError(Exception): 5 | message = "An error occurred during execution" 6 | 7 | def __init__( 8 | self, 9 | message: str = None, 10 | command: List = None, 11 | return_code: int = None, 12 | stdin_data: str = None, 13 | stdout_data: str = None, 14 | stderr_data: str = None, 15 | error_dict: Dict = None, 16 | ) -> None: 17 | if message is not None: 18 | self.message = message 19 | self.command = command or [] 20 | self.return_code = return_code 21 | self.stdin_data = stdin_data 22 | self.stderr_data = stderr_data 23 | self.stdout_data = stdout_data 24 | self.error_dict = error_dict 25 | 26 | def __str__(self) -> str: 27 | return ( 28 | f"{self.message}" 29 | f"\n> command: `{' '.join(str(i) for i in self.command)}`" 30 | f"\n> return code: `{self.return_code}`" 31 | "\n> stdout:" 32 | f"\n{self.stdout_data}" 33 | "\n> stderr:" 34 | f"\n{self.stderr_data}" 35 | ).strip() 36 | 37 | 38 | class ContractsNotFound(SolcError): 39 | message = "No contracts found during compilation" 40 | 41 | 42 | class SolcInstallationError(Exception): 43 | pass 44 | 45 | 46 | class UnknownOption(AttributeError): 47 | pass 48 | 49 | 50 | class UnknownValue(ValueError): 51 | pass 52 | 53 | 54 | class UnexpectedVersionError(Exception): 55 | pass 56 | 57 | 58 | class UnsupportedVersionError(ValueError): 59 | pass 60 | 61 | 62 | class SolcNotInstalled(Exception): 63 | pass 64 | 65 | 66 | class DownloadError(Exception): 67 | pass 68 | 69 | 70 | class UnexpectedVersionWarning(Warning): 71 | pass 72 | -------------------------------------------------------------------------------- /solcx/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cboy220/smartbugs/c37c9bec49913e75571c3c15afeb2f007c73a186/solcx/utils/__init__.py -------------------------------------------------------------------------------- /solcx/utils/lock.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tempfile 4 | import threading 5 | import getpass 6 | from pathlib import Path 7 | from typing import Any, Dict, Union 8 | 9 | if sys.platform == "win32": 10 | import msvcrt 11 | 12 | OPEN_MODE = os.O_RDWR | os.O_CREAT | os.O_TRUNC 13 | else: 14 | import fcntl 15 | 16 | NON_BLOCKING = fcntl.LOCK_EX | fcntl.LOCK_NB 17 | BLOCKING = fcntl.LOCK_EX 18 | 19 | _locks: Dict[str, Union["UnixLock", "WindowsLock"]] = {} 20 | _base_lock = threading.Lock() 21 | 22 | 23 | def get_process_lock(lock_id: str) -> Union["UnixLock", "WindowsLock"]: 24 | with _base_lock: 25 | if lock_id not in _locks: 26 | if sys.platform == "win32": 27 | _locks[lock_id] = WindowsLock(lock_id) 28 | else: 29 | _locks[lock_id] = UnixLock(lock_id) 30 | return _locks[lock_id] 31 | 32 | 33 | class _ProcessLock: 34 | """ 35 | Ensure an action is both thread-safe and process-safe. 36 | """ 37 | 38 | def __init__(self, lock_id: str) -> None: 39 | self._lock = threading.Lock() 40 | self._lock_path = Path(tempfile.gettempdir()).joinpath(f".solcx-lock-{getpass.getuser()}-{lock_id}") 41 | self._lock_file = self._lock_path.open("w") 42 | 43 | 44 | class UnixLock(_ProcessLock): 45 | def __enter__(self) -> None: 46 | self.acquire(True) 47 | 48 | def __exit__(self, *args: Any) -> None: 49 | self.release() 50 | 51 | def acquire(self, blocking: bool) -> bool: 52 | if not self._lock.acquire(blocking): 53 | return False 54 | try: 55 | fcntl.flock(self._lock_file, BLOCKING if blocking else NON_BLOCKING) 56 | except BlockingIOError: 57 | self._lock.release() 58 | return False 59 | return True 60 | 61 | def release(self) -> None: 62 | fcntl.flock(self._lock_file, fcntl.LOCK_UN) 63 | self._lock.release() 64 | 65 | 66 | class WindowsLock(_ProcessLock): 67 | def __enter__(self) -> None: 68 | self.acquire(True) 69 | 70 | def __exit__(self, *args: Any) -> None: 71 | self.release() 72 | 73 | def acquire(self, blocking: bool) -> bool: 74 | if not self._lock.acquire(blocking): 75 | return False 76 | while True: 77 | try: 78 | fd = os.open(self._lock_path, OPEN_MODE) # type: ignore 79 | msvcrt.locking( # type: ignore 80 | fd, msvcrt.LK_LOCK if blocking else msvcrt.LK_NBLCK, 1 # type: ignore 81 | ) 82 | self._fd = fd 83 | return True 84 | except OSError: 85 | if not blocking: 86 | self._lock.release() 87 | return False 88 | 89 | def release(self) -> None: 90 | msvcrt.locking(self._fd, msvcrt.LK_UNLCK, 1) # type: ignore 91 | self._lock.release() 92 | -------------------------------------------------------------------------------- /templates/results.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE results ( 2 | filename TEXT, 3 | basename TEXT, 4 | toolid TEXT, 5 | toolmode TEXT, 6 | parser_version TEXT, 7 | runid TEXT, 8 | start NUMERIC, 9 | duration NUMERIC, 10 | exit_code INTEGER, 11 | findings TEXT[], 12 | infos TEXT[], 13 | errors TEXT[], 14 | fails TEXT[], 15 | PRIMARY KEY (filename, toolid, toolmode, parser_version, runid) 16 | ); 17 | -------------------------------------------------------------------------------- /templates/scripts/example.py: -------------------------------------------------------------------------------- 1 | # This is a sample file showing how to call SmartBugs 2 | # from a Python script. 3 | 4 | import sb.smartbugs, sb.settings, sb.exceptions 5 | if __name__ == "__main__": 6 | settings = sb.settings.Settings() 7 | settings.update({ 8 | "tools": ["conkas"], 9 | "files": ["samples/simple_dao.*"], 10 | #"quiet": True # suppress output on stdout 11 | }) 12 | try: 13 | sb.smartbugs.main(settings) 14 | except sb.exceptions.SmartBugsError as e: 15 | print(f"Something didn't work: {e}") 16 | -------------------------------------------------------------------------------- /templates/tools/template/config.yaml: -------------------------------------------------------------------------------- 1 | name: ToolName # optional 2 | version: 0.3.14 # version number, commit id, ... (optional) 3 | origin: where to find more on the tool, e.g. an URL # optional 4 | info: Succinct description of your tool. # optional 5 | image: smartbugs/toolname:0.3.14 # id of Docker image (mandatory) 6 | bin: scripts # folder with programs that will be accessible in the Docker container 7 | # add the section below if the tool is able to analyse Solidity source code 8 | solidity: 9 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 10 | solc: yes 11 | # add the section below if the tool is able to analyse bytecode (deployment code) 12 | bytecode: 13 | entrypoint: "'$BIN/do_bytecode.sh' '$FILENAME' '$TIMEOUT'" 14 | # add the section below if the tool is able to analyse runtime code (deployed code) 15 | runtime: 16 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 17 | -------------------------------------------------------------------------------- /templates/tools/template/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/all/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - confuzzius 3 | - conkas 4 | - ethainter 5 | - ethor 6 | - honeybadger 7 | - maian 8 | - madmax 9 | - manticore 10 | - mythril 11 | - osiris 12 | - oyente 13 | - pakala 14 | - securify 15 | - semgrep 16 | - sfuzz 17 | - slither 18 | - smartcheck 19 | - solhint 20 | - teether 21 | - vandal 22 | -------------------------------------------------------------------------------- /tools/confuzzius/config.yaml: -------------------------------------------------------------------------------- 1 | name: ConFuzzius 2 | info: A data dependency-aware hybrid fuzzer for Ethereum smart contracts 3 | origin: https://github.com/christoftorres/ConFuzzius 4 | version: "#4315fb7 v0.0.1" 5 | image: smartbugs/confuzzius:4315fb7 6 | 7 | solidity: 8 | command: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | output: /root/results.json 10 | bin: scripts 11 | solc: yes 12 | -------------------------------------------------------------------------------- /tools/confuzzius/findings.yaml: -------------------------------------------------------------------------------- 1 | Arbitrary Memory Access: 2 | classification: SWC-124, DASP-2 3 | Assertion Failure: 4 | classification: SWC-110 5 | Block Dependency: 6 | classification: SWC-120, DASP-6 7 | Integer Overflow: 8 | classification: SWC-101, DASP-3 9 | Leaking Ether: 10 | classification: SWC-105, DASP-2 11 | Locking Ether: 12 | classification: DASP-5 13 | Reentrancy: 14 | classification: SWC-107, DASP-1 15 | Transaction Order Dependency: 16 | classification: SWC-114, DASP-7 17 | Unhandled Exception: 18 | classification: SWC-104, DASP-4 19 | Unprotected Selfdestruct: 20 | classification: SWC-106, DASP-2 21 | Unsafe Delegatecall: 22 | classification: SWC-112 23 | -------------------------------------------------------------------------------- /tools/confuzzius/parser.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import tarfile 4 | import sb.parse_utils 5 | 6 | VERSION = "2022/12/31" 7 | 8 | FINDINGS = { 9 | "Arbitrary Memory Access", 10 | "Assertion Failure", 11 | "Block Dependency", 12 | "Integer Overflow", 13 | "Leaking Ether", 14 | "Locking Ether", 15 | "Reentrancy", 16 | "Transaction Order Dependency", 17 | "Unhandled Exception", 18 | "Unprotected Selfdestruct", 19 | "Unsafe Delegatecall", 20 | } 21 | 22 | 23 | def is_relevant(line): 24 | # Remove logo when parsing exceptions 25 | return line and not ( 26 | line.startswith(" _") or 27 | line.startswith(" /") or 28 | line.startswith(" /") or 29 | line.startswith(" /") or 30 | line.startswith(" \\") ) 31 | 32 | 33 | def parse(exit_code, log, output): 34 | findings, infos = [], set() 35 | cleaned_log = filter(is_relevant, log) 36 | errors, fails = sb.parse_utils.errors_fails(exit_code, cleaned_log) 37 | 38 | for line in sb.parse_utils.discard_ANSI(log): 39 | msg = [ field.strip() for field in line.split(" - ") ] 40 | if len(msg) >= 4 and msg[2] == "ERROR": 41 | e = msg[3] 42 | if e.startswith("Validation error") and e.endswith("Sender account balance cannot afford txn (ignoring for now)"): 43 | e = "Validation error: Sender account balance cannot afford txn (ignoring for now)" 44 | errors.add(e) 45 | 46 | if output: 47 | try: 48 | with io.BytesIO(output) as o, tarfile.open(fileobj=o) as tar: 49 | file = tar.extractfile("results.json") 50 | results = json.load(file) 51 | 52 | for contract, data in results.items(): 53 | for errs in data['errors'].values(): 54 | for issue in errs: 55 | finding = { 56 | "contract": contract, 57 | "name": issue["type"], 58 | "severity": issue["severity"], 59 | "line": issue["line"], 60 | "message": f"Classification: SWC-{issue['swc_id']}" } 61 | findings.append(finding) 62 | except Exception as e: 63 | fails.add(f"error parsing results: {e}") 64 | 65 | return findings, infos, errors, fails 66 | -------------------------------------------------------------------------------- /tools/confuzzius/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | COUNT=$(echo $CONTRACTS | wc -w) 15 | [ "$COUNT" -gt 0 ] || COUNT=1 16 | 17 | OPT_CONTRACT="" 18 | if [ "$MAIN" -eq 1 ]; then 19 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 20 | COUNT=1 21 | OPT_CONTRACT="--contract $CONTRACT" 22 | else 23 | echo "Contract '$CONTRACT' not found in $FILENAME" 24 | exit 127 25 | fi 26 | fi 27 | 28 | # Fuzz each contract at least 10 seconds 29 | OPT_TIMEOUT="" 30 | TO=$(((TIMEOUT - (10 * COUNT)) / COUNT)) 31 | if [ "$TIMEOUT" -gt 0 ] && [ $TO -ge 10 ]; then 32 | OPT_TIMEOUT="--timeout $TO" 33 | fi 34 | 35 | touch /root/results.json 36 | python3 fuzzer/main.py -s "$FILENAME" --evm byzantium --results results.json --seed 1427655 $OPT_TIMEOUT $OPT_CONTRACT 37 | -------------------------------------------------------------------------------- /tools/confuzzius/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/conkas/config.yaml: -------------------------------------------------------------------------------- 1 | name: Conkas 2 | origin: https://github.com/smartbugs/conkas 3 | version: "#4e0f256" 4 | info: Conkas analyzes Ethereum smart contracts to find potential security issues. It uses Rattle to lift the bytecode to an intermediate representation and then applies symbolic execution. 5 | image: smartbugs/conkas:4e0f256 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN' '$MAIN'" 9 | solc: yes 10 | runtime: 11 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME'" 12 | -------------------------------------------------------------------------------- /tools/conkas/findings.yaml: -------------------------------------------------------------------------------- 1 | Integer Overflow: 2 | classification: SWC-101, DASP-3 3 | Integer Underflow: 4 | classification: SWC-101, DASP-3 5 | Reentrancy: 6 | classification: SWC-107, DASP-1 7 | Time Manipulation: 8 | classification: SWC-116, DASP-8 9 | Transaction Ordering Dependence: 10 | classification: SWC-114, DASP-7 11 | Unchecked Low Level Call: 12 | classification: SWC-104, DASP-4 13 | -------------------------------------------------------------------------------- /tools/conkas/parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/11" 5 | 6 | FINDINGS = { 7 | "Integer Overflow", 8 | "Integer Underflow", 9 | "Reentrancy", 10 | "Time Manipulation", 11 | "Transaction Ordering Dependence", 12 | "Unchecked Low Level Call" 13 | } 14 | 15 | ERRORS = ( 16 | re.compile("([A-Z0-9]+ instruction needs return value)"), 17 | re.compile("([A-Z0-9]+ instruction needs [0-9]+ arguments but [0-9]+ was given)"), 18 | re.compile("([A-Z0-9]+ instruction need arguments but [0-9]+ was given)"), 19 | re.compile("([A-Z0-9]+ instruction needs a concrete argument)"), 20 | re.compile("([A-Z0-9]+ instruction should not be reached)"), 21 | re.compile("([A-Z0-9]+ instruction is not implemented)"), 22 | re.compile("(Cannot get source map runtime\. Check if solc is in your path environment variable)"), 23 | re.compile("(Vulnerability module checker initialized without traces)"), 24 | re.compile(".*(solcx.exceptions.SolcError:.*)") 25 | ) 26 | 27 | ANALYSING = re.compile("^Analysing (.*)\.\.\.$") 28 | VULNERABILITY = re.compile("^Vulnerability: (.*)\. Maybe in function: (.*)\. PC: 0x(.*)\. Line number: (.*)\.$") 29 | 30 | 31 | def is_relevant(line): 32 | return not ANALYSING.match(line) 33 | 34 | 35 | def parse(exit_code, log, output): 36 | findings, infos = [], set() 37 | cleaned_log = filter(is_relevant, log) 38 | errors, fails = sb.parse_utils.errors_fails(exit_code, cleaned_log) 39 | 40 | for f in list(fails): # iterate over a copy of "fails" such that it can be modified 41 | if f.startswith("exception (KeyError: )") 44 | if f.startswith("exception (RecursionError: maximum recursion depth exceeded while calling a Python object)"): 45 | # Normalize two types of recursion errors to the shorter one. 46 | fails.remove(f) 47 | fails.add("exception (RecursionError: maximum recursion depth exceeded)") 48 | 49 | filename,contract = None,None 50 | for line in log: 51 | if sb.parse_utils.add_match(errors, line, ERRORS): 52 | fails.discard("exception (Exception)") 53 | continue 54 | m = ANALYSING.match(line) 55 | if m: 56 | filename,contract = m[1].split(":") if ":" in m[1] else (m[1],None) 57 | m = VULNERABILITY.match(line) 58 | if m: 59 | finding = { "name": m[1] } 60 | if filename: finding["filename"] = filename 61 | if contract: finding["contract"] = contract 62 | if m[2]: finding["function"] = m[2] 63 | if m[3]: finding["address"] = int(m[3],16) 64 | if m[4]: finding["line"] = int(m[4]) 65 | findings.append(finding) 66 | 67 | return findings, infos, errors, fails 68 | 69 | -------------------------------------------------------------------------------- /tools/conkas/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | 5 | cd /conkas 6 | python3 conkas.py -fav "$FILENAME" 7 | -------------------------------------------------------------------------------- /tools/conkas/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | MAIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x $BIN/solc 9 | 10 | CONTRACT="${FILENAME%.sol}" 11 | CONTRACT="${CONTRACT##*/}" 12 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 13 | 14 | OPT_CONTRACT="" 15 | if [ "$MAIN" -eq 1 ]; then 16 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 17 | OPT_CONTRACT="--contract $CONTRACT" 18 | else 19 | echo "Contract '$CONTRACT' not found in $FILENAME" 20 | exit 127 21 | fi 22 | fi 23 | 24 | cd /conkas 25 | python3 conkas.py -fav -s "$FILENAME" $OPT_CONTRACT 26 | -------------------------------------------------------------------------------- /tools/conkas/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/ethainter/config.yaml: -------------------------------------------------------------------------------- 1 | name: Ethainter 2 | origin: https://zenodo.org/record/3760403 3 | info: "Ethainter is a smart contract security analyzer for composite vulnerabilities. See the paper (Brent et al.: Ethainter: A Smart Contract Security Analyzer for Composite Vulnerabilities; In 41st ACM SIGPLAN Conference on Programming Language Design and Implementation, 2020)." 4 | image: smartbugs/ethainter:latest 5 | runtime: 6 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 7 | bin: scripts 8 | output: /home/reviewer/gigahorse-toolchain/logic/results.json 9 | -------------------------------------------------------------------------------- /tools/ethainter/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # wget https://zenodo.org/record/3760403/files/ethainter.tar.bz2 2 | # docker load -i ethainter.tar.bz2 3 | FROM ethainter 4 | 5 | # Precompile ethainter 6 | RUN ./analyze.py --reuse_datalog_bin --restart -d ../.. -j 1 -C ../../ethainter-inlined.dl; rm -rf .temp results.json ../../contracts/* 7 | 8 | USER root 9 | 10 | RUN mkdir /data; chown reviewer.reviewer /data 11 | 12 | COPY scripts/run.sh run.sh 13 | 14 | ENTRYPOINT [ "bash", "run.sh" ] 15 | -------------------------------------------------------------------------------- /tools/ethainter/docker/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | 5 | ./analyze.py --reuse_datalog_bin --restart --rerun_clients -d /data -C ../../ethainter-inlined.dl 6 | -------------------------------------------------------------------------------- /tools/ethainter/findings.yaml: -------------------------------------------------------------------------------- 1 | AccessibleSelfdestruct: 2 | classification: SWC-106, DASP-2 3 | TaintedDelegatecall: 4 | classification: SWC-112 5 | TaintedOwnerVariable: 6 | classification: SWC-124, DASP-2 7 | TaintedSelfdestruct: 8 | classification: SWC-105, DASP-2 9 | TaintedStoreIndex: 10 | classification: SWC-124, DASP-2 11 | TaintedValueSend: 12 | classification: SWC-105, DASP-2 13 | UncheckedTaintedStaticcall: {} 14 | -------------------------------------------------------------------------------- /tools/ethainter/parser.py: -------------------------------------------------------------------------------- 1 | import tools.gigahorse.parser as gigahorse 2 | 3 | VERSION = gigahorse.VERSION 4 | 5 | FINDINGS = { 6 | "TaintedStoreIndex", 7 | "TaintedSelfdestruct", 8 | "TaintedValueSend", 9 | "UncheckedTaintedStaticcall", 10 | "AccessibleSelfdestruct", 11 | "TaintedDelegatecall", 12 | "TaintedOwnerVariable" 13 | } 14 | 15 | def parse(exit_code, log, output): 16 | return gigahorse.parse(exit_code, log, output, FINDINGS) 17 | -------------------------------------------------------------------------------- /tools/ethainter/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | SB=$(dirname "$FILENAME") 6 | 7 | OPT_TIMEOUT="" 8 | if [ "$TIMEOUT" -ge 170 ]; then 9 | # reserve 50s for Ethainter to finish after timeout 10 | TO=$((TIMEOUT-50)) 11 | OPT_TIMEOUT="-T $TO" 12 | fi 13 | 14 | cd /home/reviewer/gigahorse-toolchain/logic 15 | ./analyze.py --reuse_datalog_bin --restart --rerun_clients $OPT_TIMEOUT -d "$SB" -C ../../ethainter-inlined.dl 16 | -------------------------------------------------------------------------------- /tools/ethor-2021/config.yaml: -------------------------------------------------------------------------------- 1 | name: eThor 2 | origin: https://secpriv.wien/ethor 3 | version: "2021 (CCS 2020)" 4 | info: eThor is a sound static analyzer for EVM smart contracts based on HoRSt. 5 | runtime: 6 | image: smartbugs/ethor:rmvi20q 7 | command: "ethor-with-reconstruction '$FILENAME' --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis" 8 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/README.md: -------------------------------------------------------------------------------- 1 | The docker container created by these files bundles the version of eThor used for the experiments published in CCS. 2 | 3 | ## Build 4 | 5 | ``` 6 | nix-build dockertools.nix 7 | ``` 8 | 9 | (if you know how to use docker but don't have nix-build installed, the easiest way _might be_ to get a NixOS docker image and call `nix-build` within) 10 | 11 | After `nix-build` returns, there should be a file `result` which is the image. 12 | 13 | Load it with 14 | 15 | ``` 16 | docker load -i result 17 | Loaded image: ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 # the id might be different 18 | ``` 19 | 20 | ## Run 21 | 22 | You can run the eThor tool (as described by `--help`) with 23 | 24 | ``` 25 | docker run --rm -ti -v $PWD:/mnt/ ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 ethor 26 | ``` 27 | 28 | The cfg-reconstruction tool (with a simplified interface) is also contained in the image. 29 | 30 | This call takes a contract, given as hex-encoding of it's bytecode `contract.hex` and outputs 31 | a file `contract.json` which contains the reconstructed jump destinations. 32 | 33 | ``` 34 | docker run --rm -ti -v $PWD:/mnt/ ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 run-reconstruct contract.hex contract.json 35 | ``` 36 | 37 | Finally, there is also a tool that combines the two former tools. 38 | 39 | This tool has a slightly different interface than eThor. The first argument is 40 | a single contract in hex encoding, the following parameters are passed to eThor. 41 | The following parameters may not be passed: `--smt-out-dir` 42 | `--no-output-query-result` (both are implied). 43 | 44 | What the tool does is: reconstruct the control flow for the given contract, 45 | generate a smt-file for it and then run z3 (parallely) on these file for each 46 | query. 47 | 48 | If one query return "sat", the contract is labelled insecure, otherwise if one 49 | query returns or times out, the contract is labelled unknown, otherwise the 50 | contract is labelled safe. 51 | 52 | If the environment variable `Z3_TIMEOUT` is set (via docker) `z3` will be 53 | aborted after the given time runs out. 54 | 55 | If the `EARLY_EXIT_ON_SAT` is set to `YES`, the experiment will be aborted 56 | after the first query is satisfied (as the contract will be labelled insecure 57 | in any case). 58 | 59 | ``` 60 | # execute all queries 61 | docker run --rm -ti -v $PWD:/mnt/ ethor:yqcrsvbdzhcjc8mnssfjl7fal1gx1wwp ethor-with-reconstruction 0x015a06a433353f8db634df4eddf0c109882a15ab.hex --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis 62 | 63 | # abort on first sat and abort z3 after 30 seconds 64 | docker run --rm -ti -v $PWD:/mnt/ -e TIMEOUT=30s -e EARLY_EXIT_ON_SAT=YES ethor:yqcrsvbdzhcjc8mnssfjl7fal1gx1wwp ethor-with-reconstruction 0x015a06a433353f8db634df4eddf0c109882a15ab.hex --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis 65 | ``` 66 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/dockertools.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | let 3 | reconstructCfg = import ./reconstructCfg.nix; 4 | ethor = import ./precompiled-ethor.nix; 5 | ethorWithReconstruction = import ./ethor-with-reconstruction.nix { inherit stdenv coreutils gnugrep findutils killall writeShellScriptBin reconstructCfg ethor; }; 6 | envInUsrBin = stdenv.mkDerivation { 7 | name = "env-in-usr-lib"; 8 | phases = [ "installPhase" ]; 9 | installPhase = '' 10 | mkdir -p $out/usr/bin 11 | ln -s ${coreutils}/bin/env $out/usr/bin 12 | ''; 13 | }; 14 | in 15 | dockerTools.buildImage { 16 | name = "ethor"; 17 | extraCommands = "mkdir -m 0777 tmp"; 18 | contents = buildEnv { 19 | name = "container_env"; 20 | paths = [ 21 | ethor 22 | ethorWithReconstruction 23 | reconstructCfg 24 | bashInteractive 25 | coreutils 26 | envInUsrBin 27 | ]; 28 | }; 29 | config = { 30 | Env = []; 31 | WorkingDir = "/mnt"; 32 | Cmd = [ "/bin/bash" ]; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/ethor-with-reconstruction.nix: -------------------------------------------------------------------------------- 1 | { stdenv, coreutils, gnugrep, findutils, killall, writeShellScriptBin, reconstructCfg, ethor }: 2 | 3 | stdenv.mkDerivation rec { 4 | version = "0.2"; 5 | pname = "ethor-with-reconstruction-${version}"; 6 | 7 | phases = [ "installPhase" ]; 8 | 9 | runQuery = writeShellScriptBin "run-query" '' 10 | set -u 11 | OUTDIR="$(${coreutils}/bin/mktemp -d)" 12 | OUT="$OUTDIR/out" 13 | ERR="$OUTDIR/err" 14 | FILE=$1 15 | 16 | Z3_TIMEOUT=''${Z3_TIMEOUT:-0} 17 | EARLY_EXIT_ON_SAT=''${EARLY_EXIT_ON_SAT:-NO} 18 | 19 | timeout "$Z3_TIMEOUT" ${ethor.z3}/bin/z3 "$FILE" > "$OUT" 2> "$ERR" 20 | RES=$? 21 | [ $RES != "0" ] && echo "unknown (exitcode $RES)" >> "$OUT" 22 | 23 | echo "$OUTDIR" 24 | 25 | if [ $EARLY_EXIT_ON_SAT = "YES" ]; then 26 | if ${gnugrep}/bin/grep -oE '^sat$' "$OUT" > /dev/null; then 27 | ${killall}/bin/killall z3 28 | fi 29 | fi 30 | ''; 31 | 32 | ethorWithReconstruction = writeShellScriptBin "ethor-with-reconstruction" '' 33 | set -eu 34 | HEX_FILE=$1 35 | WORK_DIR="$(${coreutils}/bin/mktemp -d)" 36 | JSON_FILE="$WORK_DIR/$(${coreutils}/bin/basename $HEX_FILE).json" 37 | RESULT_FILE="$WORK_DIR/result" 38 | 39 | ${reconstructCfg}/bin/run-reconstruct "$HEX_FILE" "$JSON_FILE" > /dev/null 40 | shift 41 | ${ethor}/bin/ethor $* --no-output-query-results --smt-out-dir "$WORK_DIR" -- "$JSON_FILE" 42 | 43 | SMT_FILE="$JSON_FILE.smt" 44 | 45 | if [ ! -f "$SMT_FILE" ]; then 46 | echo "$HEX_FILE unknown" 47 | exit 0 48 | fi 49 | 50 | ID="$(${coreutils}/bin/basename \\"$SMT_FILE\\" .json.smt)" 51 | SPLITS="$WORK_DIR/splits/" 52 | 53 | ${coreutils}/bin/mkdir -p "$SPLITS" 54 | 55 | ${gnugrep}/bin/grep '(query' "$SMT_FILE" | ${gnugrep}/bin/grep -oE 'reentrancy[^)]*' | 56 | ${findutils}/bin/xargs -I{} sh -c "${gnugrep}/bin/grep -v '(query' $SMT_FILE > $SPLITS/$ID-{}.smt; ${gnugrep}/bin/grep '(query {}' $SMT_FILE >> $SPLITS/$ID-{}.smt" 57 | 58 | ${findutils}/bin/find "$SPLITS" -type f | 59 | ${findutils}/bin/xargs -P0 -L1 ${runQuery}/bin/run-query 2> /dev/null | 60 | ${findutils}/bin/xargs -I{} sh -c '${coreutils}/bin/cat "{}/out"; ${coreutils}/bin/rm -r "{}"' > "$RESULT_FILE" 61 | 62 | if ${gnugrep}/bin/grep -oE '^sat$' "$RESULT_FILE" > /dev/null; then 63 | echo "$HEX_FILE insecure" 64 | elif ${gnugrep}/bin/grep -oE '^unknown' "$RESULT_FILE" > /dev/null; then 65 | echo "$HEX_FILE unknown" 66 | else 67 | echo "$HEX_FILE secure" 68 | fi 69 | 70 | ${coreutils}/bin/rm -r "$WORK_DIR" 71 | ''; 72 | 73 | installPhase = '' 74 | cp -r ${ethorWithReconstruction} $out 75 | ''; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/precompiled-ethor.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | let 3 | version = "0.2"; 4 | in 5 | stdenv.mkDerivation rec { 6 | name = "ethor-${version}"; 7 | z3 = import ./z3.nix { inherit stdenv lib fetchFromGitHub python jdk; }; 8 | 9 | src = fetchurl { 10 | url = "https://owncloud.tuwien.ac.at/index.php/s/aQZhNxkCutDqHTl/download"; 11 | sha256 = "0fg9j8yjjbp9c4ygb9w5lq4qjs754i9gaa3mxya5fhc138y5fp20"; 12 | }; 13 | 14 | jdk = pkgs.openjdk8_headless; 15 | 16 | phases = [ "installPhase" ]; 17 | buildInputs = [ jdk z3 pkgs.makeWrapper ]; 18 | 19 | installPhase = '' 20 | mkdir -p $out/bin/ 21 | mkdir -p $out/lib/ 22 | 23 | cp ${src} $out/lib/ethor.jar 24 | 25 | echo ${jdk}/bin/java -Xmx16G -jar $out/lib/ethor.jar '"$@"' > $out/bin/ethor 26 | chmod +x $out/bin/ethor 27 | 28 | wrapProgram $out/bin/ethor --prefix LD_LIBRARY_PATH : ${z3.lib}/lib/ 29 | ''; 30 | 31 | meta = with lib; { 32 | description = "A static analyzer using Horn clauses."; 33 | license = licenses.gpl3; 34 | platforms = [ "x86_64-linux" ]; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/reconstructCfg.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | let 3 | souffleThor = stdenv.mkDerivation rec { 4 | pname = "souffleThor"; 5 | version = "0.1"; 6 | src = fetchurl { 7 | url = "https://owncloud.tuwien.ac.at/index.php/s/ctL6MTorCuYsV5N/download"; 8 | sha256 = "0ng16z4g6s8sfrmkczsg2j8gvklxq7xbxgyzx1d2xbi564zdnwrj"; 9 | }; 10 | 11 | buildInputs = with pkgs; [ unzip ]; 12 | 13 | phases = ["unpackPhase" "patchPhase" "installPhase"]; 14 | 15 | patches = [ ./reconstruction.patch ]; 16 | 17 | patchFlags = [ "-p0" ]; 18 | 19 | unpackPhase = '' 20 | unzip $src 21 | ''; 22 | 23 | installPhase = '' 24 | mkdir $out 25 | mv * $out 26 | ''; 27 | }; 28 | in 29 | stdenv.mkDerivation rec { 30 | pname = "reconstruct-cfg"; 31 | version = "0.1"; 32 | 33 | phases = [ "installPhase" ]; 34 | 35 | buildInputs = with pkgs; [ openjdk8_headless souffle python3 makeWrapper ]; 36 | 37 | runReconstruct = writeShellScriptBin "run-reconstruct" '' 38 | set -eu 39 | INPUT_DIR=$(${mktemp}/bin/mktemp -d) 40 | OUTPUT_DIR=$(${mktemp}/bin/mktemp -d) 41 | INPUT_FILE="$1" 42 | INPUT_NAME="$(${coreutils}/bin/basename "$INPUT_FILE" .txt)" 43 | OUTPUT_FILE="$2" 44 | 45 | ${coreutils}/bin/cp "$INPUT_FILE" "$INPUT_DIR/$INPUT_NAME.txt" 46 | 47 | ${souffleThor}/script/reconstruct.sh "$INPUT_DIR" "$OUTPUT_DIR" 48 | 49 | ${coreutils}/bin/cp "$OUTPUT_DIR/"* "$OUTPUT_FILE" 50 | 51 | ${coreutils}/bin/rm -r "$INPUT_DIR" 52 | ${coreutils}/bin/rm -r "$OUTPUT_DIR" 53 | ''; 54 | 55 | installPhase = '' 56 | makeWrapper ${runReconstruct}/bin/run-reconstruct $out/bin/run-reconstruct \ 57 | --prefix PATH : ${openjdk8_headless}/bin \ 58 | --prefix PATH : ${souffle}/bin \ 59 | --prefix PATH : ${python3}/bin 60 | ''; 61 | } 62 | -------------------------------------------------------------------------------- /tools/ethor-2021/docker/z3.nix: -------------------------------------------------------------------------------- 1 | { stdenv, lib, fetchFromGitHub, python, jdk }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "z3"; 5 | version = "4.8.4-908-ga2b18a37e"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "Z3Prover"; 9 | repo = pname; 10 | rev = "z3-${version}"; 11 | sha256 = "1wizp61ls43s71r3kp4jx9qyh2bbji8007w25qbfh6sr5jbv68ha"; 12 | }; 13 | 14 | buildInputs = [ python jdk ]; 15 | enableParallelBuilding = true; 16 | 17 | configurePhase = '' 18 | ${python.interpreter} scripts/mk_make.py --prefix=$out --java 19 | cd build 20 | ''; 21 | 22 | postInstall = '' 23 | mkdir -p $dev $lib $java 24 | mv $out/lib $lib/lib 25 | mv $out/include $dev/include 26 | ''; 27 | 28 | outputs = [ "out" "lib" "dev" ]; 29 | 30 | meta = { 31 | description = "A high-performance theorem prover and SMT solver"; 32 | homepage = "https://github.com/Z3Prover/z3"; 33 | license = lib.licenses.mit; 34 | platforms = lib.platforms.x86_64; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /tools/ethor-2021/findings.yaml: -------------------------------------------------------------------------------- 1 | insecure: 2 | classification: SWC-107, DASP-1 3 | secure: {} 4 | -------------------------------------------------------------------------------- /tools/ethor-2021/parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/11" 5 | 6 | FINDINGS = ("secure", "insecure") 7 | 8 | UNKNOWN_BYTECODE = "Encountered an unknown bytecode" 9 | 10 | FAILS = ( 11 | re.compile("OpenJDK.* failed; error='([^']+)'"), 12 | re.compile("(Floating-point arithmetic exception) signal in rule"), 13 | re.compile(".*(Undefined relation [a-zA-Z0-9]+) in file .*dl at line"), 14 | ) 15 | 16 | UNSUPPORTED_OP = re.compile(".*(java.lang.UnsupportedOperationException: [^)]*)\)") 17 | 18 | COMPLETED = re.compile("^(.*) (secure|insecure|unknown)$") 19 | 20 | def parse(exit_code, log, output): 21 | findings, infos = [], set() 22 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 23 | errors.discard('EXIT_CODE_1') # redundant: exit code 1 is reflected in other errors 24 | if 'DOCKER_TIMEOUT' in fails or 'DOCKER_KILL_OOM' in fails: 25 | fails.discard('exception (Killed)') 26 | # "Unsupported Op" is a regular, checked-for errors, not an unexpected fails 27 | for e in list(fails): 28 | m = UNSUPPORTED_OP.match(e) 29 | if m: 30 | fails.remove(e) 31 | errors.add(m[1]) 32 | 33 | analysis_complete = False 34 | for line in log: 35 | if UNKNOWN_BYTECODE in line: 36 | infos.add(UNKNOWN_BYTECODE) 37 | continue 38 | if sb.parse_utils.add_match(fails, line, FAILS): 39 | continue 40 | if line.endswith(" unknown"): 41 | analysis_complete = True 42 | continue 43 | m = COMPLETED.match(line) 44 | if m: 45 | analysis_complete = True 46 | if m[2] in FINDINGS: 47 | findings.append({"filename": m[1], "name": m[2]}) 48 | continue 49 | if "DOCKER_SEGV" in fails: 50 | fails.discard("exception (Segmentation fault)") 51 | 52 | if log and not analysis_complete: 53 | infos.add('analysis incomplete') 54 | if not fails and not errors: 55 | fails.add('execution failed') 56 | 57 | return findings, infos, errors, fails 58 | -------------------------------------------------------------------------------- /tools/ethor-2023/config.yaml: -------------------------------------------------------------------------------- 1 | name: eThor 2 | origin: https://secpriv.wien/ethor 3 | version: "2023" 4 | info: eThor is a sound static analyzer for EVM smart contracts based on HoRSt. 5 | runtime: 6 | image: smartbugs/ethor:2023 7 | command: "ethor-with-reconstruction '$FILENAME' --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis -v" 8 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/README.md: -------------------------------------------------------------------------------- 1 | The docker container created by these files bundles the version of eThor used for the experiments published in CCS with some minor patches applied: 2 | 3 | ## Changes 4 | * support for EXTCODEHASH 5 | * control flow reconstruction does not crash on the superfluous ANDs introduced by some versions of Solidity 6 | * update souffle version (the last version SEGFAULTed on some inputs) 7 | 8 | ## Build 9 | 10 | ``` 11 | NIXPKGS_ALLOW_INSECURE=1 nix build --impure . 12 | ``` 13 | 14 | The `NIXPKGS_ALLOW_INSECURE=1` environment variable is needed, since the bundled version of z3 has `python2` as build dependency, which is marked insecure in nixpkgs. 15 | 16 | (if you know how to use docker but don't have nix installed, the easiest way _might be_ to get a NixOS docker image and call `nix build` within) 17 | 18 | After `nix build` returns, there should be a file `result` which is the image. 19 | 20 | Load it with 21 | 22 | ``` 23 | docker load -i result 24 | Loaded image: ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 # the id might be different 25 | ``` 26 | 27 | ## Run 28 | 29 | You can run the eThor tool (as described by `--help`) with 30 | 31 | ``` 32 | docker run --rm -ti -v $PWD:/mnt/ ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 ethor 33 | ``` 34 | 35 | The cfg-reconstruction tool (with a simplified interface) is also contained in the image. 36 | 37 | This call takes a contract, given as hex-encoding of it's bytecode `contract.hex` and outputs 38 | a file `contract.json` which contains the reconstructed jump destinations. 39 | 40 | ``` 41 | docker run --rm -ti -v $PWD:/mnt/ ethor:ddj39dq9p5x7a3fskva810lzq5sdmv90 run-reconstruct contract.hex contract.json 42 | ``` 43 | 44 | Finally, there is also a tool that combines the two former tools. 45 | 46 | This tool has a slightly different interface than eThor. The first argument is 47 | a single contract in hex encoding, the following parameters are passed to eThor. 48 | The following parameters may not be passed: `--smt-out-dir` 49 | `--no-output-query-result` (both are implied). 50 | 51 | What the tool does is: reconstruct the control flow for the given contract, 52 | generate a smt-file for it and then run z3 (parallely) on these file for each 53 | query. 54 | 55 | If one query return "sat", the contract is labelled insecure, otherwise if one 56 | query returns or times out, the contract is labelled unknown, otherwise the 57 | contract is labelled safe. 58 | 59 | If the environment variable `Z3_TIMEOUT` is set (via docker) `z3` will be 60 | aborted after the given time runs out. 61 | 62 | If the `EARLY_EXIT_ON_SAT` is set to `YES`, the experiment will be aborted 63 | after the first query is satisfied (as the contract will be labelled insecure 64 | in any case). 65 | 66 | ``` 67 | # execute all queries 68 | docker run --rm -ti -v $PWD:/mnt/ ethor:yqcrsvbdzhcjc8mnssfjl7fal1gx1wwp ethor-with-reconstruction 0x015a06a433353f8db634df4eddf0c109882a15ab.hex --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis 69 | 70 | # abort on first sat and abort z3 after 30 seconds 71 | docker run --rm -ti -v $PWD:/mnt/ -e TIMEOUT=30s -e EARLY_EXIT_ON_SAT=YES ethor:yqcrsvbdzhcjc8mnssfjl7fal1gx1wwp ethor-with-reconstruction 0x015a06a433353f8db634df4eddf0c109882a15ab.hex --prune-strategy=aggressive --predicate-inlining-strategy=linear --preanalysis 72 | ``` 73 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/dockertools.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | let 3 | reconstructCfg = import ./reconstructCfg.nix { inherit pkgs; }; 4 | ethor = import ./precompiled-ethor.nix; 5 | # ethorWithReconstruction = import ./ethor-with-reconstruction.nix { inherit stdenv coreutils gnugrep findutils killall writeShellScriptBin reconstructCfg ethor; }; 6 | envInUsrBin = stdenv.mkDerivation { 7 | name = "env-in-usr-lib"; 8 | phases = [ "installPhase" ]; 9 | installPhase = '' 10 | mkdir -p $out/usr/bin 11 | ln -s ${coreutils}/bin/env $out/usr/bin 12 | ''; 13 | }; 14 | in 15 | dockerTools.buildImage { 16 | name = "ethor"; 17 | extraCommands = "mkdir -m 0777 tmp"; 18 | contents = buildEnv { 19 | name = "container_env"; 20 | paths = [ 21 | ethor 22 | # ethorWithReconstruction 23 | # reconstructCfg 24 | bashInteractive 25 | coreutils 26 | envInUsrBin 27 | ]; 28 | }; 29 | config = { 30 | Env = []; 31 | WorkingDir = "/mnt"; 32 | Cmd = [ "/bin/bash" ]; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/ethor-with-reconstruction.nix: -------------------------------------------------------------------------------- 1 | { stdenv, coreutils, gnugrep, findutils, killall, writeShellScriptBin, reconstructCfg, ethor }: 2 | 3 | stdenv.mkDerivation rec { 4 | version = "0.2"; 5 | pname = "ethor-with-reconstruction-${version}"; 6 | 7 | phases = [ "installPhase" ]; 8 | 9 | runQuery = writeShellScriptBin "run-query" '' 10 | set -u 11 | OUTDIR="$(${coreutils}/bin/mktemp -d)" 12 | OUT="$OUTDIR/out" 13 | ERR="$OUTDIR/err" 14 | FILE=$1 15 | 16 | Z3_TIMEOUT=''${Z3_TIMEOUT:-0} 17 | EARLY_EXIT_ON_SAT=''${EARLY_EXIT_ON_SAT:-NO} 18 | 19 | timeout "$Z3_TIMEOUT" ${ethor.z3}/bin/z3 "$FILE" > "$OUT" 2> "$ERR" 20 | RES=$? 21 | [ $RES != "0" ] && echo "unknown (exitcode $RES)" >> "$OUT" 22 | 23 | echo "$OUTDIR" 24 | 25 | if [ $EARLY_EXIT_ON_SAT = "YES" ]; then 26 | if ${gnugrep}/bin/grep -oE '^sat$' "$OUT" > /dev/null; then 27 | ${killall}/bin/killall z3 28 | fi 29 | fi 30 | ''; 31 | 32 | ethorWithReconstruction = writeShellScriptBin "ethor-with-reconstruction" '' 33 | set -eu 34 | HEX_FILE=$1 35 | WORK_DIR="$(${coreutils}/bin/mktemp -d)" 36 | JSON_FILE="$WORK_DIR/$(${coreutils}/bin/basename $HEX_FILE).json" 37 | RESULT_FILE="$WORK_DIR/result" 38 | 39 | ${reconstructCfg}/bin/run-reconstruct "$HEX_FILE" "$JSON_FILE" > /dev/null 40 | shift 41 | ${ethor}/bin/ethor $* --no-output-query-results --smt-out-dir "$WORK_DIR" -- "$JSON_FILE" 42 | 43 | SMT_FILE="$JSON_FILE.smt" 44 | 45 | if [ ! -f "$SMT_FILE" ]; then 46 | echo "$HEX_FILE unknown" 47 | exit 0 48 | fi 49 | 50 | ID="$(${coreutils}/bin/basename \\"$SMT_FILE\\" .json.smt)" 51 | SPLITS="$WORK_DIR/splits/" 52 | 53 | ${coreutils}/bin/mkdir -p "$SPLITS" 54 | 55 | ${gnugrep}/bin/grep '(query' "$SMT_FILE" | ${gnugrep}/bin/grep -oE 'reentrancy[^)]*' | 56 | ${findutils}/bin/xargs -I{} sh -c "${gnugrep}/bin/grep -v '(query' $SMT_FILE > $SPLITS/$ID-{}.smt; ${gnugrep}/bin/grep '(query {}' $SMT_FILE >> $SPLITS/$ID-{}.smt" 57 | 58 | ${findutils}/bin/find "$SPLITS" -type f | 59 | ${findutils}/bin/xargs -P0 -L1 ${runQuery}/bin/run-query 2> /dev/null | 60 | ${findutils}/bin/xargs -I{} sh -c '${coreutils}/bin/cat "{}/out"; ${coreutils}/bin/rm -r "{}"' > "$RESULT_FILE" 61 | 62 | if ${gnugrep}/bin/grep -oE '^sat$' "$RESULT_FILE" > /dev/null; then 63 | echo "$HEX_FILE insecure" 64 | elif ${gnugrep}/bin/grep -oE '^unknown' "$RESULT_FILE" > /dev/null; then 65 | echo "$HEX_FILE unknown" 66 | else 67 | echo "$HEX_FILE secure" 68 | fi 69 | 70 | ${coreutils}/bin/rm -r "$WORK_DIR" 71 | ''; 72 | 73 | installPhase = '' 74 | cp -r ${ethorWithReconstruction} $out 75 | ''; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1700444282, 6 | "narHash": "sha256-s/+tgT+Iz0LZO+nBvSms+xsMqvHt2LqYniG9r+CYyJc=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "3f21a22b5aafefa1845dec6f4a378a8f53d8681c", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "type": "indirect" 15 | } 16 | }, 17 | "root": { 18 | "inputs": { 19 | "nixpkgs": "nixpkgs" 20 | } 21 | } 22 | }, 23 | "root": "root", 24 | "version": 7 25 | } 26 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Ethor and related tools"; 3 | 4 | outputs = { self, nixpkgs }: 5 | let 6 | pkgs = import nixpkgs { system = "x86_64-linux"; }; 7 | in { 8 | 9 | packages.x86_64-linux.hello = 10 | let 11 | reconstructCfg = import ./reconstructCfg.nix { inherit pkgs; }; 12 | ethor = import ./precompiled-ethor.nix { inherit pkgs; }; 13 | ethorWithReconstruction = import ./ethor-with-reconstruction.nix { 14 | inherit (pkgs) stdenv coreutils gnugrep findutils killall writeShellScriptBin; 15 | inherit reconstructCfg ethor; 16 | }; 17 | envInUsrBin = pkgs.stdenv.mkDerivation { 18 | name = "env-in-usr-lib"; 19 | phases = [ "installPhase" ]; 20 | installPhase = '' 21 | mkdir -p $out/usr/bin 22 | ln -s ${pkgs.coreutils}/bin/env $out/usr/bin 23 | ''; 24 | }; 25 | in pkgs.dockerTools.buildImage { 26 | name = "ethor"; 27 | extraCommands = "mkdir -m 0777 tmp"; 28 | contents = pkgs.buildEnv { 29 | name = "container_env"; 30 | paths = [ 31 | ethor 32 | ethorWithReconstruction 33 | reconstructCfg 34 | pkgs.bashInteractive 35 | pkgs.coreutils 36 | envInUsrBin 37 | ]; 38 | }; 39 | config = { 40 | Env = [ ]; 41 | WorkingDir = "/mnt"; 42 | Cmd = [ "/bin/bash" ]; 43 | }; 44 | }; 45 | 46 | defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello; 47 | packages.x86_64-linux.ethor = import ./precompiled-ethor.nix { inherit pkgs; }; 48 | packages.x86_64-linux.reconstructCfg = import ./reconstructCfg.nix { inherit pkgs; }; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/precompiled-ethor.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: 2 | let 3 | version = "0.2"; 4 | in 5 | with pkgs; stdenv.mkDerivation rec { 6 | name = "ethor-${version}"; 7 | z3 = import ./z3.nix { inherit stdenv lib fetchFromGitHub python jdk; }; 8 | 9 | src = fetchurl { 10 | url = "https://owncloud.tuwien.ac.at/index.php/s/aQZhNxkCutDqHTl/download"; 11 | sha256 = "0fg9j8yjjbp9c4ygb9w5lq4qjs754i9gaa3mxya5fhc138y5fp20"; 12 | }; 13 | 14 | jdk = pkgs.openjdk8_headless; 15 | 16 | phases = [ "installPhase" ]; 17 | buildInputs = [ jdk z3 pkgs.makeWrapper ]; 18 | 19 | installPhase = '' 20 | mkdir -p $out/bin/ 21 | mkdir -p $out/lib/ 22 | 23 | cp ${src} $out/lib/ethor.jar 24 | 25 | echo ${jdk}/bin/java -Xmx16G -jar $out/lib/ethor.jar '"$@"' > $out/bin/ethor 26 | chmod +x $out/bin/ethor 27 | 28 | wrapProgram $out/bin/ethor --prefix LD_LIBRARY_PATH : ${z3.lib}/lib/ 29 | ''; 30 | 31 | meta = with lib; { 32 | description = "A static analyzer using Horn clauses."; 33 | license = licenses.gpl3; 34 | platforms = [ "x86_64-linux" ]; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/reconstructCfg.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: 2 | with pkgs; 3 | let 4 | souffleThor = stdenv.mkDerivation rec { 5 | pname = "souffleThor"; 6 | version = "0.1"; 7 | src = fetchurl { 8 | url = "https://owncloud.tuwien.ac.at/index.php/s/ctL6MTorCuYsV5N/download"; 9 | sha256 = "0ng16z4g6s8sfrmkczsg2j8gvklxq7xbxgyzx1d2xbi564zdnwrj"; 10 | }; 11 | 12 | buildInputs = with pkgs; [ unzip ]; 13 | 14 | phases = ["unpackPhase" "patchPhase" "installPhase"]; 15 | 16 | patches = [ ./reconstruction.patch ]; 17 | 18 | patchFlags = [ "-p0" ]; 19 | 20 | unpackPhase = '' 21 | unzip $src 22 | ''; 23 | 24 | installPhase = '' 25 | mkdir $out 26 | mv * $out 27 | ''; 28 | }; 29 | in 30 | stdenv.mkDerivation rec { 31 | pname = "reconstruct-cfg"; 32 | version = "0.1"; 33 | 34 | phases = [ "installPhase" ]; 35 | 36 | buildInputs = with pkgs; [ openjdk8_headless souffle python3 makeWrapper ]; 37 | 38 | runReconstruct = writeShellScriptBin "run-reconstruct" '' 39 | set -eu 40 | INPUT_DIR=$(${mktemp}/bin/mktemp -d) 41 | OUTPUT_DIR=$(${mktemp}/bin/mktemp -d) 42 | INPUT_FILE="$1" 43 | INPUT_NAME="$(${coreutils}/bin/basename "$INPUT_FILE" .txt)" 44 | OUTPUT_FILE="$2" 45 | 46 | ${coreutils}/bin/cp "$INPUT_FILE" "$INPUT_DIR/$INPUT_NAME.txt" 47 | 48 | ${souffleThor}/script/reconstruct.sh "$INPUT_DIR" "$OUTPUT_DIR" 49 | 50 | ${coreutils}/bin/cp "$OUTPUT_DIR/"* "$OUTPUT_FILE" 51 | 52 | ${coreutils}/bin/rm -r "$INPUT_DIR" 53 | ${coreutils}/bin/rm -r "$OUTPUT_DIR" 54 | ''; 55 | 56 | installPhase = '' 57 | makeWrapper ${runReconstruct}/bin/run-reconstruct $out/bin/run-reconstruct \ 58 | --prefix PATH : ${openjdk8_headless}/bin \ 59 | --prefix PATH : ${souffle}/bin \ 60 | --prefix PATH : ${python3}/bin 61 | ''; 62 | } 63 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/reconstruction.patch: -------------------------------------------------------------------------------- 1 | --- script/evm-abstract-semantics-light.dl 2022-01-10 15:47:55.076241068 +0100 2 | +++ evm-abstract-semantics-light-new.dl 2022-01-10 15:47:31.367928794 +0100 3 | @@ -42,6 +42,7 @@ 4 | .decl BALANCE(pc: number) 5 | .decl CALLDATALOAD(pc: number) 6 | .decl EXTCODESIZE(pc: number) 7 | +.decl EXTCODEHASH(pc: number) 8 | .decl BLOCKHASH(pc: number) 9 | .decl MLOAD(pc: number) 10 | .decl SLOAD(pc: number) 11 | @@ -148,7 +149,7 @@ BINOP(pc) :- GT(pc). 12 | BINOP(pc) :- SLT(pc). 13 | BINOP(pc) :- SGT(pc). 14 | BINOP(pc) :- EQ(pc). 15 | -BINOP(pc) :- AND(pc). 16 | +//BINOP(pc) :- AND(pc). 17 | BINOP(pc) :- OR(pc). 18 | BINOP(pc) :- XOR(pc). 19 | BINOP(pc) :- BYTE(pc). 20 | @@ -180,6 +181,7 @@ POPZEROPUSHONE(pc) :- GAS(pc). 21 | POPONEPUSHONE(pc) :- BALANCE(pc). 22 | POPONEPUSHONE(pc) :- CALLDATALOAD(pc). 23 | POPONEPUSHONE(pc) :- EXTCODESIZE(pc). 24 | +POPONEPUSHONE(pc) :- EXTCODEHASH(pc). 25 | POPONEPUSHONE(pc) :- BLOCKHASH(pc). 26 | POPONEPUSHONE(pc) :- MLOAD(pc). 27 | POPONEPUSHONE(pc) :- SLOAD(pc). 28 | @@ -257,6 +259,15 @@ State(pc + 1, [value2, [value3, [value4, [value5, [value6, [value7, [value8, [va 29 | // POP 30 | State(pc + 1, stack) :- State(pc, [_,stack]), POP(pc). 31 | 32 | +State(pc + 1, ["T",stack]) :- State(pc, ["T", [_,stack]]), AND(pc). 33 | + 34 | +State(pc + 1, ["T",stack]) :- State(pc, [_, ["T",stack]]), AND(pc). 35 | + 36 | +State(pc + 1, [X ,stack]) :- State(pc, [X, ["4294967295",stack]]), AND(pc). 37 | +State(pc + 1, [X ,stack]) :- State(pc, ["4294967295", [X,stack]]), AND(pc). 38 | + 39 | +State(pc + 1, ["T" ,stack]) :- State(pc, [Y, [X, stack]]), AND(pc), Y != "4294967295", X != "4294967295". 40 | + 41 | State(pc + 1, ["T",stack]) :- State(pc, [_, [_,stack]]), BINOP(pc). 42 | 43 | State(pc + 1, ["T",stack]) :- State(pc, [_,stack]), UNOP(pc). 44 | -------------------------------------------------------------------------------- /tools/ethor-2023/docker/z3.nix: -------------------------------------------------------------------------------- 1 | { stdenv, lib, fetchFromGitHub, python, jdk }: 2 | 3 | stdenv.mkDerivation rec { 4 | pname = "z3"; 5 | version = "4.8.4-908-ga2b18a37e"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "Z3Prover"; 9 | repo = pname; 10 | rev = "z3-${version}"; 11 | sha256 = "1wizp61ls43s71r3kp4jx9qyh2bbji8007w25qbfh6sr5jbv68ha"; 12 | }; 13 | 14 | buildInputs = [ python jdk ]; 15 | enableParallelBuilding = true; 16 | 17 | configurePhase = '' 18 | ${python.interpreter} scripts/mk_make.py --prefix=$out --java 19 | cd build 20 | ''; 21 | 22 | postInstall = '' 23 | mkdir -p $dev $lib $java 24 | mv $out/lib $lib/lib 25 | mv $out/include $dev/include 26 | ''; 27 | 28 | outputs = [ "out" "lib" "dev" ]; 29 | 30 | meta = { 31 | description = "A high-performance theorem prover and SMT solver"; 32 | homepage = "https://github.com/Z3Prover/z3"; 33 | license = lib.licenses.mit; 34 | platforms = lib.platforms.x86_64; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /tools/ethor-2023/findings.yaml: -------------------------------------------------------------------------------- 1 | insecure: 2 | classification: SWC-107, DASP-1 3 | secure: {} 4 | -------------------------------------------------------------------------------- /tools/ethor-2023/parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/11" 5 | 6 | FINDINGS = ("secure", "insecure") 7 | 8 | UNKNOWN_BYTECODE = "Encountered an unknown bytecode" 9 | 10 | FAILS = ( 11 | re.compile("OpenJDK.* failed; error='([^']+)'"), 12 | re.compile("(Floating-point arithmetic exception) signal in rule"), 13 | re.compile(".*(Undefined relation [a-zA-Z0-9]+) in file .*dl at line"), 14 | ) 15 | 16 | UNSUPPORTED_OP = re.compile(".*(java.lang.UnsupportedOperationException: [^)]*)\\)") 17 | 18 | COMPLETED = re.compile("^(.*) (secure|insecure|unknown)$") 19 | 20 | def parse(exit_code, log, output): 21 | findings, infos = [], set() 22 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 23 | errors.discard('EXIT_CODE_1') # redundant: exit code 1 is reflected in other errors 24 | if 'DOCKER_TIMEOUT' in fails or 'DOCKER_KILL_OOM' in fails: 25 | fails.discard('exception (Killed)') 26 | # "Unsupported Op" is a regular, checked-for errors, not an unexpected fails 27 | for e in list(fails): 28 | m = UNSUPPORTED_OP.match(e) 29 | if m: 30 | fails.remove(e) 31 | errors.add(m[1]) 32 | 33 | analysis_complete = False 34 | for line in log: 35 | if UNKNOWN_BYTECODE in line: 36 | infos.add(UNKNOWN_BYTECODE) 37 | continue 38 | if sb.parse_utils.add_match(fails, line, FAILS): 39 | continue 40 | if line.endswith(" unknown"): 41 | analysis_complete = True 42 | continue 43 | m = COMPLETED.match(line) 44 | if m: 45 | analysis_complete = True 46 | if m[2] in FINDINGS: 47 | findings.append({"filename": m[1], "name": m[2]}) 48 | continue 49 | if "DOCKER_SEGV" in fails: 50 | fails.discard("exception (Segmentation fault)") 51 | 52 | if log and not analysis_complete: 53 | infos.add('analysis incomplete') 54 | if not fails and not errors: 55 | fails.add('execution failed') 56 | 57 | return findings, infos, errors, fails 58 | -------------------------------------------------------------------------------- /tools/ethor/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - ethor-2023 3 | -------------------------------------------------------------------------------- /tools/gigahorse/parser.py: -------------------------------------------------------------------------------- 1 | import io, json, tarfile 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/17" 5 | 6 | def parse(exit_code, log, output, FINDINGS): 7 | findings, infos = [], set() 8 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 9 | 10 | if "Writing results to results.json" not in log: 11 | infos.add("analysis incomplete") 12 | if not fails and not errors: 13 | fails.add("execution failed") 14 | 15 | try: 16 | with io.BytesIO(output) as o, tarfile.open(fileobj=o) as tar: 17 | results_json=tar.extractfile("results.json").read() 18 | result = json.loads(results_json) 19 | for contract in result: 20 | filename = contract[0] 21 | errors.update(contract[2]) 22 | report = contract[3] 23 | for name in FINDINGS: 24 | if not report.get(name): 25 | continue 26 | addresses = [] 27 | for address in report[name].split(): 28 | address = address.lower() 29 | i = 2 if address[:2] == "0x" else 0 30 | while i < len(address): 31 | if address[i] not in "0123456789abcdef": 32 | break 33 | i += 1 34 | try: 35 | addresses.append(int(address[0:i],16)) 36 | except: 37 | pass 38 | if addresses: 39 | for address in addresses: 40 | findings.append({ 41 | "filename": filename, 42 | "name": name, 43 | "address": address 44 | }) 45 | else: 46 | findings.append({ 47 | "filename": filename, 48 | "name": name 49 | }) 50 | 51 | 52 | 53 | except Exception as e: 54 | fails.add(f"problem extracting results.json from docker container: {e}") 55 | 56 | return findings, infos, errors, fails 57 | -------------------------------------------------------------------------------- /tools/honeybadger/config.yaml: -------------------------------------------------------------------------------- 1 | name: HoneyBadger 2 | version: "#ff30c9a" 3 | info: An analysis tool to detect honeypots in Ethereum smart contracts 4 | origin: https://github.com/christoftorres/HoneyBadger 5 | image: smartbugs/honeybadger:ff30c9a 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | runtime: 11 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 12 | -------------------------------------------------------------------------------- /tools/honeybadger/findings.yaml: -------------------------------------------------------------------------------- 1 | Balance disorder: {} 2 | Hidden state update: {} 3 | Hidden transfer: {} 4 | Inheritance disorder: {} 5 | Money flow: {} 6 | Skip empty string: {} 7 | Straw man contract: {} 8 | Type overflow: {} 9 | Uninitialised struct: {} 10 | -------------------------------------------------------------------------------- /tools/honeybadger/parser.py: -------------------------------------------------------------------------------- 1 | import tools.oyente.parser as oyente 2 | 3 | VERSION = oyente.VERSION 4 | 5 | FINDINGS = { 6 | "Money flow", 7 | "Balance disorder", 8 | "Hidden transfer", 9 | "Inheritance disorder", 10 | "Uninitialised struct", 11 | "Type overflow", 12 | "Skip empty string", 13 | "Hidden state update", 14 | "Straw man contract" 15 | } 16 | 17 | def parse(exit_code, log, output): 18 | return oyente.parse(exit_code, log, output) 19 | -------------------------------------------------------------------------------- /tools/honeybadger/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for honeybadger to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="-glt $TO" 12 | fi 13 | 14 | python honeybadger/honeybadger.py $OPT_TIMEOUT -b -s "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/honeybadger/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT="--contract $CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for honeybadger to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="-glt $TO" 31 | fi 32 | 33 | python honeybadger/honeybadger.py $OPT_TIMEOUT -s "$FILENAME" $OPT_CONTRACT 34 | -------------------------------------------------------------------------------- /tools/honeybadger/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/madmax/config.yaml: -------------------------------------------------------------------------------- 1 | name: MadMax 2 | origin: https://github.com/nevillegrech/MadMax 3 | version: "#6e9a6e9" 4 | info: Madmax consists of a series of analyses and queries that find gas-focused vulnerabilities in Ethereum smart contracts. 5 | image: smartbugs/madmax:6e9a6e9 6 | runtime: 7 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 8 | bin: scripts 9 | output: /MadMax/results.json 10 | -------------------------------------------------------------------------------- /tools/madmax/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image 2 | FROM ubuntu:20.04 3 | 4 | ENV LC_ALL C.UTF-8 5 | RUN apt update; DEBIAN_FRONTEND=noninteractive apt install -y libboost-all-dev git python3 bison \ 6 | build-essential \ 7 | clang \ 8 | cmake \ 9 | doxygen \ 10 | flex \ 11 | g++ \ 12 | libffi-dev \ 13 | libncurses5 \ 14 | libncurses5-dev \ 15 | libsqlite3-dev \ 16 | make \ 17 | mcpp \ 18 | sqlite \ 19 | zlib1g-dev \ 20 | libtinfo5 21 | 22 | RUN apt -y install wget 23 | RUN wget https://github.com/souffle-lang/souffle/releases/download/2.0.2/souffle_2.0.2-1_amd64.deb -O /tmp/souffle.deb 24 | RUN wget http://archive.ubuntu.com/ubuntu/pool/main/libf/libffi/libffi6_3.2.1-8_amd64.deb -O /tmp/libffi6.deb 25 | RUN dpkg -i /tmp/libffi6.deb 26 | RUN dpkg -i /tmp/souffle.deb 27 | 28 | COPY scripts/gitconfig /home/root/.gitconfig 29 | 30 | # Clone the repository 31 | RUN git -c "include.path=/home/root/.gitconfig" clone --recursive https://github.com/nevillegrech/MadMax.git MadMax 32 | WORKDIR /MadMax 33 | RUN git checkout 6e9a6e9 34 | 35 | RUN cd gigahorse-toolchain/souffle-addon; make 36 | 37 | # Precompile madmax.dl etc. 38 | RUN python3 gigahorse-toolchain/gigahorse.py -C madmax.dl gigahorse-toolchain/examples/long_running.hex; rm -rf .temp results.json 39 | 40 | COPY scripts/run.sh run.sh 41 | 42 | ENTRYPOINT [ "bash", "run.sh" ] 43 | -------------------------------------------------------------------------------- /tools/madmax/docker/scripts/gitconfig: -------------------------------------------------------------------------------- 1 | [url "https://github.com/"] 2 | insteadOf = git@github.com: -------------------------------------------------------------------------------- /tools/madmax/docker/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME=$1 4 | CODE=$(cat ${FILENAME}) 5 | 6 | python3 gigahorse-toolchain/gigahorse.py --reuse_datalog_bin --rerun_clients --restart -C madmax.dl ${FILENAME} 7 | -------------------------------------------------------------------------------- /tools/madmax/findings.yaml: -------------------------------------------------------------------------------- 1 | OverflowLoopIterator: 2 | classification: SWC-101, DASP-3 3 | UnboundedMassOp: 4 | classification: SWC-128, DASP-5 5 | WalletGriefing: 6 | classification: SWC-113, DASP-5 7 | -------------------------------------------------------------------------------- /tools/madmax/parser.py: -------------------------------------------------------------------------------- 1 | import tools.gigahorse.parser as gigahorse 2 | 3 | VERSION = gigahorse.VERSION 4 | 5 | FINDINGS = { 6 | "OverflowLoopIterator", 7 | "UnboundedMassOp", 8 | "WalletGriefing" 9 | } 10 | 11 | def parse(exit_code, log, output): 12 | return gigahorse.parse(exit_code, log, output, FINDINGS) 13 | -------------------------------------------------------------------------------- /tools/madmax/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -ge 170 ]; then 8 | # reserve 50s for MaxMax to finish after timeout 9 | TO=$((TIMEOUT-50)) 10 | OPT_TIMEOUT="-T $TO" 11 | fi 12 | 13 | cd /MadMax 14 | python3 gigahorse-toolchain/gigahorse.py --reuse_datalog_bin --rerun_clients --restart $OPT_TIMEOUT -C madmax.dl "${FILENAME}" 15 | -------------------------------------------------------------------------------- /tools/maian/config.yaml: -------------------------------------------------------------------------------- 1 | name: Maian 2 | origin: https://github.com/smartbugs/MAIAN 3 | info: Maian is a tool for the automated detection of buggy Ethereum smart contracts of type 'prodigal', 'suicidal', and 'greedy'. 4 | image: smartbugs/maian:solc5.10 5 | version: '#4bab09a' 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN' '$MAIN'" 9 | solc: yes 10 | bytecode: 11 | entrypoint: "'$BIN/do_bytecode.sh' '$FILENAME'" 12 | runtime: 13 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME'" 14 | -------------------------------------------------------------------------------- /tools/maian/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Docker Image with MAIAN (Smartbugs fork) 2 | 3 | # Pull base image 4 | FROM ubuntu:20.04 5 | 6 | # Update packages, install build essentials, wget and Python 7 | RUN \ 8 | apt update; \ 9 | DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 10 | tzdata build-essential software-properties-common libssl-dev wget git \ 11 | z3 psmisc lsof wget python3-pip python3-dev; \ 12 | pip install --upgrade pip 13 | 14 | # Install GETH version 1.10.14 15 | RUN \ 16 | wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.14-11a3a350.tar.gz && \ 17 | tar zxvf geth-linux-amd64-1.10.14-11a3a350.tar.gz && \ 18 | cp geth-linux-amd64-1.10.14-11a3a350/geth /usr/local/bin && \ 19 | chmod +x /usr/local/bin/geth 20 | 21 | # Precompute ethash data 22 | RUN \ 23 | mkdir ~/.ethash; \ 24 | for block in 0 30000; do \ 25 | geth makedag $block ~/.ethash; \ 26 | done 27 | 28 | # Script that extracts contract names 29 | ENV LC_ALL C.UTF-8 30 | RUN pip3 install solidity_parser 31 | COPY scripts/printContractNames.py printContractNames.py 32 | 33 | # Scripts that Smartbugs calls 34 | COPY scripts/runMAIANall.sh runMAIANall.sh 35 | COPY scripts/run_maian_solidity.sh run_maian_solidity.sh 36 | COPY scripts/run_maian_bytecode.sh run_maian_bytecode.sh 37 | RUN chmod +x printContractNames.py run_maian_solidity.sh run_maian_bytecode.sh runMAIANall.sh 38 | 39 | # Install MAIAN from GitHub, precompile and test it 40 | ARG MAIAN_COMMIT=master 41 | RUN \ 42 | git clone https://github.com/smartbugs/MAIAN; \ 43 | cd /MAIAN; \ 44 | git checkout $MAIAN_COMMIT; \ 45 | pip3 install -r requirements-3.8.txt; \ 46 | cd tool; \ 47 | for c in 0 1 2; do \ 48 | python3 maian.py -bs example_contracts/example_suicidal.bytecode_source -c $c; \ 49 | done; \ 50 | rm -rf out/* 51 | 52 | # Install Solidity compiler 53 | ARG SOLC_VERSION=0.5.10 54 | RUN \ 55 | wget https://github.com/ethereum/solidity/releases/download/v$SOLC_VERSION/solc-static-linux && \ 56 | mv solc-static-linux /usr/bin/solc && \ 57 | chmod +x /usr/bin/solc 58 | 59 | ENV SOLC /usr/bin/solc 60 | 61 | -------------------------------------------------------------------------------- /tools/maian/docker/README.txt: -------------------------------------------------------------------------------- 1 | # Latest Maian with solc 0.5.10 2 | docker build . 3 | #... 4 | #Successfully built 5 | docker image rm smartbugs/maian:solc5.10 6 | docker tag smartbugs/maian:solc5.10 7 | docker push smartbugs/maian:solc5.10 8 | 9 | # Latest Maian with solc 0.4.25 10 | docker build --build-arg SOLC_VERSION=0.4.25 . 11 | #... 12 | #Successfully built 13 | docker image rm smartbugs/maian:solc4.25 14 | docker tag smartbugs/maian:solc4.25 15 | docker push smartbugs/maian:solc4.25 16 | 17 | # Specific Maian with specific Solc 18 | docker build --build-arg MAIAN_COMMIT= --build-arg SOLC_VERSION= . 19 | #... 20 | #Successfully built 21 | docker image rm smartbugs/maian: 22 | docker tag smartbugs/maian: 23 | docker push smartbugs/maian: 24 | -------------------------------------------------------------------------------- /tools/maian/docker/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pprint 3 | 4 | from solidity_parser import parser 5 | 6 | sourceUnit = parser.parse_file(sys.argv[1]) 7 | #pprint.pprint(sourceUnit) 8 | 9 | sourceUnitObject = parser.objectify(sourceUnit) 10 | 11 | # access imports, contracts, functions, ... (see outline example in 12 | # __main__.py) 13 | sourceUnitObject.imports # [] 14 | sourceUnitObject.pragmas # [] 15 | for contract_name in sourceUnitObject.contracts.keys(): 16 | print(contract_name) 17 | -------------------------------------------------------------------------------- /tools/maian/docker/scripts/runMAIANall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME=$1 4 | 5 | for c in `python3 printContractNames.py ${FILENAME} | grep -v "ANTLR runtime and generated code versions disagree:"`; 6 | do 7 | cd /MAIAN/tool; 8 | python3 maian.py -c 0 -s ${FILENAME} ${c}; 9 | python3 maian.py -c 1 -s ${FILENAME} ${c}; 10 | python3 maian.py -c 2 -s ${FILENAME} ${c} 11 | done 12 | -------------------------------------------------------------------------------- /tools/maian/docker/scripts/run_maian_bytecode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME=$1 4 | 5 | cd /MAIAN/tool 6 | python3 maian.py -c 0 -b ${FILENAME} 7 | python3 maian.py -c 1 -b ${FILENAME} 8 | python3 maian.py -c 2 -b ${FILENAME} 9 | -------------------------------------------------------------------------------- /tools/maian/docker/scripts/run_maian_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME=$1 4 | 5 | for c in `python3 printContractNames.py ${FILENAME} | grep -v "ANTLR runtime and generated code versions disagree:"`; 6 | do 7 | cd /MAIAN/tool; 8 | python3 maian.py -c 0 -s ${FILENAME} ${c}; 9 | python3 maian.py -c 1 -s ${FILENAME} ${c}; 10 | python3 maian.py -c 2 -s ${FILENAME} ${c} 11 | done 12 | -------------------------------------------------------------------------------- /tools/maian/findings.yaml: -------------------------------------------------------------------------------- 1 | Destructible: 2 | classification: SWC-106, DASP-2 3 | Destructible (verified): 4 | classification: SWC-106, DASP-2 5 | Ether leak: 6 | classification: SWC-105, DASP-2 7 | Ether leak (verified): 8 | classification: SWC-105, DASP-2 9 | Ether lock: 10 | classification: DASP-5 11 | Ether lock (Ether accepted without send): 12 | classification: DASP-5 13 | No Ether leak (no send): {} 14 | No Ether lock (Ether refused): {} 15 | Not destructible (no self-destruct): {} 16 | -------------------------------------------------------------------------------- /tools/maian/scripts/do_bytecode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | 5 | cd /MAIAN/tool 6 | python3 maian.py -c 0 -bs "$FILENAME" 7 | python3 maian.py -c 1 -bs "$FILENAME" 8 | python3 maian.py -c 2 -bs "$FILENAME" 9 | -------------------------------------------------------------------------------- /tools/maian/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | 5 | cd /MAIAN/tool 6 | python3 maian.py -c 0 -b "$FILENAME" 7 | python3 maian.py -c 1 -b "$FILENAME" 8 | python3 maian.py -c 2 -b "$FILENAME" 9 | -------------------------------------------------------------------------------- /tools/maian/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | MAIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x $BIN/solc 9 | 10 | CONTRACT="${FILENAME%.sol}" 11 | CONTRACT="${CONTRACT##*/}" 12 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 13 | 14 | if [ "$MAIN" -eq 1 ]; then 15 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 16 | CONTRACTS="$CONTRACT" 17 | else 18 | echo "Contract '$CONTRACT' not found in $FILENAME" 19 | exit 127 20 | fi 21 | fi 22 | 23 | cd /MAIAN/tool; 24 | for CONTRACT in $CONTRACTS; do 25 | for c in 0 1 2; do 26 | python3 maian.py -c "$c" -s "$FILENAME" "$CONTRACT" 27 | done 28 | done 29 | -------------------------------------------------------------------------------- /tools/maian/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/manticore-0.3.7/config.yaml: -------------------------------------------------------------------------------- 1 | name: Manticore 2 | origin: https://github.com/trailofbits/manticore 3 | version: 0.3.7 4 | info: Manticore is a symbolic execution tool for analysis of smart contracts and binaries. 5 | image: smartbugs/manticore:0.3.7 6 | output: /results 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN'" 9 | solc: yes 10 | bin: scripts 11 | -------------------------------------------------------------------------------- /tools/manticore-0.3.7/parser.py: -------------------------------------------------------------------------------- 1 | import io, tarfile, yaml 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/17" 5 | 6 | FINDINGS = set() 7 | 8 | def parse_file(lines): 9 | findings = [] 10 | snippet = False 11 | for line in lines: 12 | if snippet: 13 | snippet = False 14 | l = line.split() 15 | finding["line"] = int(l[0]) 16 | finding["code"] = c 17 | elif line.startwith(" Solidity snippet:"): 18 | snippet = True 19 | elif line[0] == "-": 20 | finding = { 21 | "name": line[1:-2].strip() 22 | } 23 | findings.append(finding) 24 | continue 25 | return findings 26 | 27 | 28 | def parse(exit_code, log, output): 29 | findings, infos = [], set() 30 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 31 | 32 | if any("Invalid solc compilation" in line for line in log): 33 | errors.add("solc error") 34 | 35 | try: 36 | with io.BytesIO(output) as o, tarfile.open(fileobj=o) as tar: 37 | for fn in tar.getnames(): 38 | if not fn.endswith("/global.findings"): 39 | continue 40 | 41 | try: 42 | contents = tar.extractfile(fn).read() 43 | manticore_findings = parse_file(contents.splitlines()) 44 | except Exception as e: 45 | fails.add(f"problem extracting {fn} from output archive: {e}") 46 | continue 47 | 48 | cmd = None 49 | try: 50 | fn = fn.replace("/global.findings","/manticore.yml") 51 | cmd = yaml.safe_load(tar.extractfile(fn).read()) 52 | except Exception as e: 53 | infos.add(f"manticore.yml not found") 54 | 55 | filename, contract = None, None 56 | if isinstance(cmd,dict): 57 | cli = cmd.get("cli") 58 | if isinstance(cli,dict): 59 | contract = cli.get("contract") 60 | argv = cli.get("argv") 61 | if isinstance(argv,list) and len(argv) > 0: 62 | filename = argv[0] 63 | 64 | for mf in manticore_findings: 65 | if filename: 66 | mf["filename"] = filename 67 | if contract: 68 | mf["contract"] = contract 69 | findings.append(mf) 70 | except Exception as e: 71 | fails.add(f"error parsing results: {e}") 72 | 73 | return findings, infos, errors, fails 74 | -------------------------------------------------------------------------------- /tools/manticore-0.3.7/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | 6 | export PATH="$BIN:$PATH" 7 | chmod +x "$BIN/solc" 8 | 9 | mkdir /results 10 | 11 | for c in `python3 "$BIN/printContractNames.py" "${FILENAME}"`; do 12 | manticore --no-colors --contract "${c}" "${FILENAME#/}" 13 | mv /mcore_* /results 14 | done 15 | -------------------------------------------------------------------------------- /tools/manticore-0.3.7/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/manticore/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - manticore-0.3.7 3 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/config.yaml: -------------------------------------------------------------------------------- 1 | name: Mythril 2 | version: 0.23.15 3 | origin: https://github.com/ConsenSys/mythril 4 | info: Mythril analyses EVM bytecode using symbolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities. 5 | image: smartbugs/mythril:0.23.15 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | bytecode: 11 | entrypoint: "'$BIN/do_bytecode.sh' '$FILENAME' '$TIMEOUT'" 12 | runtime: 13 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 14 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/parser.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sb.parse_utils 3 | 4 | VERSION = "2023/01/20" 5 | 6 | FINDINGS = { 7 | "Jump to an arbitrary instruction (SWC 127)", 8 | "Write to an arbitrary storage location (SWC 124)", 9 | "Delegatecall to user-supplied address (SWC 112)", 10 | "Dependence on tx.origin (SWC 115)", 11 | "Dependence on predictable environment variable (SWC 116)", 12 | "Dependence on predictable environment variable (SWC 120)", 13 | "Unprotected Ether Withdrawal (SWC 105)", 14 | "Exception State (SWC 110)", 15 | "External Call To User-Supplied Address (SWC 107)", 16 | "Integer Arithmetic Bugs (SWC 101)", 17 | "Multiple Calls in a Single Transaction (SWC 113)", 18 | "State access after external call (SWC 107)", 19 | "Unprotected Selfdestruct (SWC 106)", 20 | "Unchecked return value from external call. (SWC 104)", 21 | } 22 | 23 | 24 | def parse(exit_code, log, output): 25 | 26 | findings, infos = [], set() 27 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 28 | errors.discard("EXIT_CODE_1") # exit code = 1 just means that a weakness has been found 29 | 30 | # Mythril catches all exceptions, prints a message "please report", and then prints the traceback. 31 | # So we consider all exceptions as fails (= non-intended interruptions) 32 | for f in list(fails): # iterate over a copy of 'fails' such that it can be modified 33 | if f.startswith("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal"): 34 | fails.remove(f) 35 | fails.add("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal)") 36 | 37 | for line in log: 38 | if "Exception occurred, aborting analysis." in line: 39 | infos.add("analysis incomplete") 40 | if not fails and not errors: 41 | fails.add("execution failed") 42 | break 43 | 44 | try: 45 | result = json.loads(log[-1]) 46 | except: 47 | result = None 48 | if result: 49 | error = result.get("error") 50 | if error: 51 | errors.add(error.split('.')[0]) 52 | for issue in result.get("issues", []): 53 | finding = { "name": issue["title"] } 54 | for i,f in ( ("filename","filename"), ("contract","contract"), 55 | ("function","function"), ("address","address"), ("lineno", "line"), 56 | ("tx_sequence","exploit"), ("description","message"), ("severity","severity") ): 57 | if i in issue: 58 | finding[f] = issue[i] 59 | if "swc-id" in issue: 60 | finding["name"] += f" (SWC {issue['swc-id']})" 61 | classification = f"Classification: SWC-{issue['swc-id']}" 62 | if finding.get("message"): 63 | finding["message"] += f"\n{classification}" 64 | else: 65 | finding["message"] = classification 66 | findings.append(finding) 67 | 68 | return findings, infos, errors, fails 69 | 70 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/scripts/do_bytecode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json --bin-runtime -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT=":$CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for mythril to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="--execution-timeout $TO" 31 | fi 32 | 33 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json "$FILENAME$OPT_CONTRACT" 34 | -------------------------------------------------------------------------------- /tools/mythril-0.23.15/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/config.yaml: -------------------------------------------------------------------------------- 1 | name: Mythril 2 | version: 0.23.5 3 | origin: https://github.com/ConsenSys/mythril 4 | info: Mythril analyses EVM bytecode using symbolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities. 5 | image: smartbugs/mythril:0.23.5 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | bytecode: 11 | entrypoint: "'$BIN/do_bytecode.sh' '$FILENAME' '$TIMEOUT'" 12 | runtime: 13 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 14 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/parser.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sb.parse_utils 3 | 4 | VERSION = "2023/01/20" 5 | 6 | FINDINGS = { 7 | "Jump to an arbitrary instruction (SWC 127)", 8 | "Write to an arbitrary storage location (SWC 124)", 9 | "Delegatecall to user-supplied address (SWC 112)", 10 | "Dependence on tx.origin (SWC 115)", 11 | "Dependence on predictable environment variable (SWC 116)", 12 | "Dependence on predictable environment variable (SWC 120)", 13 | "Unprotected Ether Withdrawal (SWC 105)", 14 | "Exception State (SWC 110)", 15 | "External Call To User-Supplied Address (SWC 107)", 16 | "Integer Arithmetic Bugs (SWC 101)", 17 | "Multiple Calls in a Single Transaction (SWC 113)", 18 | "State access after external call (SWC 107)", 19 | "Unprotected Selfdestruct (SWC 106)", 20 | "Unchecked return value from external call. (SWC 104)", 21 | } 22 | 23 | 24 | def parse(exit_code, log, output): 25 | 26 | findings, infos = [], set() 27 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 28 | errors.discard("EXIT_CODE_1") # exit code = 1 just means that a weakness has been found 29 | 30 | # Mythril catches all exceptions, prints a message "please report", and then prints the traceback. 31 | # So we consider all exceptions as fails (= non-intended interruptions) 32 | for f in list(fails): # iterate over a copy of 'fails' such that it can be modified 33 | if f.startswith("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal"): 34 | fails.remove(f) 35 | fails.add("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal)") 36 | 37 | for line in log: 38 | if "Exception occurred, aborting analysis." in line: 39 | infos.add("analysis incomplete") 40 | if not fails and not errors: 41 | fails.add("execution failed") 42 | break 43 | 44 | try: 45 | result = json.loads(log[-1]) 46 | except: 47 | result = None 48 | if result: 49 | error = result.get("error") 50 | if error: 51 | errors.add(error.split('.')[0]) 52 | for issue in result.get("issues", []): 53 | finding = { "name": issue["title"] } 54 | for i,f in ( ("filename","filename"), ("contract","contract"), 55 | ("function","function"), ("address","address"), ("lineno", "line"), 56 | ("tx_sequence","exploit"), ("description","message"), ("severity","severity") ): 57 | if i in issue: 58 | finding[f] = issue[i] 59 | if "swc-id" in issue: 60 | finding["name"] += f" (SWC {issue['swc-id']})" 61 | classification = f"Classification: SWC-{issue['swc-id']}" 62 | if finding.get("message"): 63 | finding["message"] += f"\n{classification}" 64 | else: 65 | finding["message"] = classification 66 | findings.append(finding) 67 | 68 | return findings, infos, errors, fails 69 | 70 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/scripts/do_bytecode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json --bin-runtime -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT=":$CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for mythril to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="--execution-timeout $TO" 31 | fi 32 | 33 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json "$FILENAME$OPT_CONTRACT" 34 | -------------------------------------------------------------------------------- /tools/mythril-0.23.5/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/config.yaml: -------------------------------------------------------------------------------- 1 | name: Mythril 2 | version: 0.24.7 3 | origin: https://github.com/ConsenSys/mythril 4 | info: Mythril analyses EVM bytecode using symbolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities. 5 | image: smartbugs/mythril:0.24.7 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | bytecode: 11 | entrypoint: "'$BIN/do_bytecode.sh' '$FILENAME' '$TIMEOUT'" 12 | runtime: 13 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 14 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/parser.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sb.parse_utils 3 | 4 | VERSION = "2024/03/24" 5 | 6 | FINDINGS = { 7 | "Jump to an arbitrary instruction (SWC 127)", 8 | "Write to an arbitrary storage location (SWC 124)", 9 | "Delegatecall to user-supplied address (SWC 112)", 10 | "Dependence on tx.origin (SWC 115)", 11 | "Dependence on predictable environment variable (SWC 116)", 12 | "Dependence on predictable environment variable (SWC 120)", 13 | "Unprotected Ether Withdrawal (SWC 105)", 14 | "Exception State (SWC 110)", 15 | "External Call To User-Supplied Address (SWC 107)", 16 | "Integer Arithmetic Bugs (SWC 101)", 17 | "Multiple Calls in a Single Transaction (SWC 113)", 18 | "State access after external call (SWC 107)", 19 | "Unprotected Selfdestruct (SWC 106)", 20 | "Unchecked return value from external call. (SWC 104)", 21 | "Transaction Order Dependence (SWC 114)", 22 | "requirement violation (SWC 123)", 23 | "Strict Ether balance check (SWC 132)" 24 | } 25 | 26 | 27 | def parse(exit_code, log, output): 28 | 29 | findings, infos = [], set() 30 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 31 | errors.discard("EXIT_CODE_1") # exit code = 1 just means that a weakness has been found 32 | 33 | # Mythril catches all exceptions, prints a message "please report", and then prints the traceback. 34 | # So we consider all exceptions as fails (= non-intended interruptions) 35 | for f in list(fails): # iterate over a copy of 'fails' such that it can be modified 36 | if f.startswith("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal"): 37 | fails.remove(f) 38 | fails.add("exception (mythril.laser.ethereum.transaction.transaction_models.TransactionEndSignal)") 39 | 40 | for line in log: 41 | if "Exception occurred, aborting analysis." in line: 42 | infos.add("analysis incomplete") 43 | if not fails and not errors: 44 | fails.add("execution failed") 45 | break 46 | 47 | try: 48 | result = json.loads(log[-1]) 49 | except: 50 | result = None 51 | if result: 52 | error = result.get("error") 53 | if error: 54 | errors.add(error.split('.')[0]) 55 | for issue in result.get("issues", []): 56 | finding = { "name": issue["title"] } 57 | for i,f in ( ("filename","filename"), ("contract","contract"), 58 | ("function","function"), ("address","address"), ("lineno", "line"), 59 | ("tx_sequence","exploit"), ("description","message"), ("severity","severity") ): 60 | if i in issue: 61 | finding[f] = issue[i] 62 | if "swc-id" in issue: 63 | finding["name"] += f" (SWC {issue['swc-id']})" 64 | classification = f"Classification: SWC-{issue['swc-id']}" 65 | if finding.get("message"): 66 | finding["message"] += f"\n{classification}" 67 | else: 68 | finding["message"] = classification 69 | findings.append(finding) 70 | 71 | return findings, infos, errors, fails 72 | 73 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/scripts/do_bytecode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for mythril to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="--execution-timeout $TO" 12 | fi 13 | 14 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json --bin-runtime -f "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT=":$CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for mythril to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="--execution-timeout $TO" 31 | fi 32 | 33 | /usr/local/bin/myth analyze $OPT_TIMEOUT -o json "$FILENAME$OPT_CONTRACT" 34 | -------------------------------------------------------------------------------- /tools/mythril-0.24.7/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/mythril/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - mythril-0.24.7 3 | -------------------------------------------------------------------------------- /tools/osiris/config.yaml: -------------------------------------------------------------------------------- 1 | name: Osiris 2 | version: "#d1ecc37" 3 | info: Osiris is an analysis tool to detect integer bugs in Ethereum smart contracts. Osiris is based on Oyente. 4 | origin: https://github.com/christoftorres/Osiris 5 | image: smartbugs/osiris:d1ecc37 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | runtime: 11 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 12 | -------------------------------------------------------------------------------- /tools/osiris/findings.yaml: -------------------------------------------------------------------------------- 1 | Callstack bug: 2 | classification: DASP-5 3 | Concurrency bug: 4 | classification: SWC-114, DASP-7 5 | Division bugs: 6 | classification: DASP-3 7 | Modulo bugs: 8 | classification: DASP-3 9 | Overflow bugs: 10 | classification: SWC-101, DASP-3 11 | Reentrancy bug: 12 | classification: SWC-107, DASP-1 13 | Signedness bugs: 14 | classification: DASP-3 15 | Time dependency bug: 16 | classification: SWC-116, DASP-8 17 | Timedependency bug: 18 | classification: SWC-116, DASP-8 19 | Truncation bugs: 20 | classification: DASP-3 21 | Underflow bugs: 22 | classification: SWC-101, DASP-3 23 | -------------------------------------------------------------------------------- /tools/osiris/parser.py: -------------------------------------------------------------------------------- 1 | import tools.oyente.parser as oyente 2 | 3 | VERSION = oyente.VERSION 4 | 5 | FINDINGS = { 6 | # "Arithmetic bugs", # redundant, a sub-category will be reported anyway 7 | "Overflow bugs", 8 | "Underflow bugs", 9 | "Division bugs", 10 | "Modulo bugs", 11 | "Truncation bugs", 12 | "Signedness bugs", 13 | "Callstack bug", 14 | "Concurrency bug", 15 | "Timedependency bug", 16 | "Time dependency bug", 17 | "Reentrancy bug", 18 | } 19 | 20 | def parse(exit_code, log, output): 21 | return oyente.parse(exit_code, log, output) 22 | -------------------------------------------------------------------------------- /tools/osiris/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for osiris to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="-glt $TO" 12 | fi 13 | 14 | python osiris/osiris.py $OPT_TIMEOUT -b -s "$FILENAME" 15 | -------------------------------------------------------------------------------- /tools/osiris/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT="--contract $CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for osiris to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="-glt $TO" 31 | fi 32 | 33 | python osiris/osiris.py $OPT_TIMEOUT -s "$FILENAME" $OPT_CONTRACT 34 | -------------------------------------------------------------------------------- /tools/osiris/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/oyente/config.yaml: -------------------------------------------------------------------------------- 1 | name: Oyente 2 | version: "#480e725" 3 | origin: https://github.com/smartbugs/oyente 4 | info: Oyente runs on symbolic execution, determines which inputs cause which program branches to execute, to find potential security vulnerabilities. Oyente works directly with EVM bytecode without access high level representation and does not provide soundness nor completeness. 5 | image: smartbugs/oyente:480e725 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | solc: yes 10 | runtime: 11 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 12 | -------------------------------------------------------------------------------- /tools/oyente/docker/README.txt: -------------------------------------------------------------------------------- 1 | # smartbugs-oyente 2 | 3 | Oyente Image is based the commit `480e725` of https://github.com/smartbugs/oyente. This fork contains fixes to make the Dockerfile compatible with the current version of CryticCompile. 4 | -------------------------------------------------------------------------------- /tools/oyente/findings.yaml: -------------------------------------------------------------------------------- 1 | Callstack Depth Attack Vulnerability: 2 | classification: DASP-5 3 | Integer Overflow: 4 | classification: SWC-101, DASP-3 5 | Integer Underflow: 6 | classification: SWC-101, DASP-3 7 | Parity Multisig Bug 2: {} 8 | Re-Entrancy Vulnerability: 9 | classification: SWC-107, DASP-1 10 | Timestamp Dependency: 11 | classification: SWC-116, DASP-8 12 | Transaction-Ordering Dependence (TOD): 13 | classification: SWC-114, DASP-7 14 | -------------------------------------------------------------------------------- /tools/oyente/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | OPT_TIMEOUT="" 7 | if [ "$TIMEOUT" -gt 0 ]; then 8 | # TO = TIMEOUT * 80% 9 | # the remaining 20% are for Oyente to finish 10 | TO=$(( (TIMEOUT*8+9)/10 )) 11 | OPT_TIMEOUT="-glt $TO" 12 | fi 13 | 14 | cd /oyente 15 | /oyente/oyente/oyente.py $OPT_TIMEOUT -b -s "$FILENAME" 16 | -------------------------------------------------------------------------------- /tools/oyente/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | 15 | OPT_CONTRACT="" 16 | if [ "$MAIN" -eq 1 ]; then 17 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 18 | OPT_CONTRACT="--target-contracts $CONTRACT" 19 | else 20 | echo "Contract '$CONTRACT' not found in $FILENAME" 21 | exit 127 22 | fi 23 | fi 24 | 25 | OPT_TIMEOUT="" 26 | if [ "$TIMEOUT" -gt 0 ]; then 27 | # TO = TIMEOUT * 80% 28 | # the remaining 20% are for Oyente to finish 29 | TO=$(( (TIMEOUT*8+9)/10 )) 30 | OPT_TIMEOUT="-glt $TO" 31 | fi 32 | 33 | cd /oyente 34 | /oyente/oyente/oyente.py $OPT_TIMEOUT -s "$FILENAME" $OPT_CONTRACT 35 | -------------------------------------------------------------------------------- /tools/oyente/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/pakala/config.yaml: -------------------------------------------------------------------------------- 1 | name: Pakala 2 | origin: https://github.com/palkeo/pakala 3 | version: "#c84ef38 v1.1.10" 4 | info: Pakala is a tool to search for exploitable bugs in Ethereum smart contracts and a symbolic execution engine for the Ethereum Virtual Machine. 5 | image: smartbugs/pakala:1.1.10 6 | runtime: 7 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT'" 8 | bin: scripts 9 | -------------------------------------------------------------------------------- /tools/pakala/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image 2 | FROM ubuntu:20.04 3 | 4 | # Update packages, install build essentials, wget and Python 5 | RUN \ 6 | apt update; \ 7 | DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 8 | tzdata build-essential software-properties-common libssl-dev wget git \ 9 | z3 psmisc lsof wget python3 python3-pip python3-dev; \ 10 | pip install --upgrade pip 11 | 12 | # Install GETH version 1.10.14 13 | RUN \ 14 | wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.14-11a3a350.tar.gz && \ 15 | tar zxvf geth-linux-amd64-1.10.14-11a3a350.tar.gz && \ 16 | cp geth-linux-amd64-1.10.14-11a3a350/geth /usr/local/bin && \ 17 | chmod +x /usr/local/bin/geth && \ 18 | rm geth-linux-amd64-1.10.14-11a3a350.tar.gz 19 | 20 | # Update pip 21 | RUN pip3 --no-cache-dir install --upgrade setuptools pip 22 | 23 | # Install pakala 24 | RUN pip3 install pakala 25 | 26 | # Script that calls Pakala 27 | COPY scripts/runPakala.sh runPakala.sh 28 | RUN chmod +x runPakala.sh 29 | -------------------------------------------------------------------------------- /tools/pakala/docker/scripts/runPakala.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | 5 | geth --dev --http --http.api eth,web3,personal,net & 6 | sleep 2 7 | 8 | export WEB3_PROVIDER_URI="http://127.0.0.1:8545" 9 | cat "$FILENAME" | pakala - --exec-timeout 1500 --analysis-timeout 300 10 | -------------------------------------------------------------------------------- /tools/pakala/findings.yaml: -------------------------------------------------------------------------------- 1 | call bug: 2 | classification: SWC-105, DASP-2 3 | delegatecall bug: 4 | classification: SWC-112 5 | selfdestruct bug: 6 | classification: SWC-105, DASP-2 7 | -------------------------------------------------------------------------------- /tools/pakala/parser.py: -------------------------------------------------------------------------------- 1 | import re, ast 2 | import sb.parse_utils 3 | 4 | VERSION = "2023/02/27" 5 | 6 | FINDINGS = { 7 | "delegatecall bug", 8 | "selfdestruct bug", 9 | "call bug" 10 | } 11 | 12 | FINDING = re.compile(".*pakala\\.analyzer\\[.*\\] INFO Found (.* bug)\\.") 13 | COVERAGE = re.compile("Symbolic execution finished with coverage (.*).") 14 | FINISHED = re.compile("Nothing to report.|======> Bug found! Need .* transactions. <======") 15 | TRANSACTION = re.compile("Transaction [0-9]+, example solution:") 16 | 17 | def is_relevant(line): 18 | return not ( 19 | line.startswith("Analyzing contract at") 20 | or line.startswith("Starting symbolic execution step...") 21 | or line.startswith("Symbolic execution finished with coverage") 22 | or line.startswith("Outcomes: ") 23 | ) 24 | 25 | def parse(exit_code, log, output): 26 | findings, infos = [], set() 27 | cleaned_log = filter(is_relevant, log) 28 | errors, fails = sb.parse_utils.errors_fails(exit_code, cleaned_log) 29 | errors.discard("EXIT_CODE_1") # there will be an exception in fails anyway 30 | 31 | analysis_completed = False 32 | in_tx = False 33 | for line in log: 34 | if in_tx: 35 | if line: 36 | tx_dict += line 37 | else: 38 | in_tx = False 39 | try: 40 | tx = ast.literal_eval(tx_dict) 41 | if not "exploit" in finding: 42 | finding["exploit"] = [] 43 | finding["exploit"].append(tx) 44 | except Exception: 45 | pass 46 | 47 | m = TRANSACTION.match(line) 48 | if m: 49 | in_tx = True 50 | tx_dict = "" 51 | continue 52 | 53 | m = FINDING.match(line) 54 | if m: 55 | finding = { "name": m[1] } 56 | findings.append(finding) 57 | continue 58 | 59 | if FINISHED.match(line): 60 | analysis_completed = True 61 | if log and not analysis_completed: 62 | infos.add("analysis incomplete") 63 | if not fails and not errors: 64 | fails.add("execution failed") 65 | 66 | return findings, infos, errors, fails 67 | -------------------------------------------------------------------------------- /tools/pakala/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | 6 | geth --dev --http --http.api eth,web3,personal,net & 7 | sleep 2 8 | export WEB3_PROVIDER_URI="http://127.0.0.1:8545" 9 | 10 | if [ "$TIMEOUT" -eq 0 ]; then 11 | cat "$FILENAME" | pakala - 12 | else 13 | # TO1 = TIMEOUT * 80% 14 | # TO2 = TIMEOUT * 17% 15 | TO1=$(( TIMEOUT*8/10 )) 16 | TO2=$(( TIMEOUT*17/100 )) 17 | cat "$FILENAME" | pakala - --exec-timeout ${TO1} --analysis-timeout ${TO2} 18 | fi 19 | -------------------------------------------------------------------------------- /tools/securify/config.yaml: -------------------------------------------------------------------------------- 1 | name: Securify 2 | origin: https://github.com/eth-sri/securify 3 | info: Securify uses formal verification, also relying on static analysis checks. Securify's analysis consists of two steps. First, it symbolically analyzes the contract's dependency graph to extract precise semantic information from the code. Then, it checks compliance and violation patterns that capture sufficient conditions for proving if a property holds or not. 4 | image: smartbugs/securify:usolc # includes solc 0.5.11, but we don't need it 5 | #image: smartbugs/securify:0.4.25 # includes solc 0.4.24, but we don't need it 6 | output: /results/ 7 | bin: scripts 8 | solidity: 9 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN'" 10 | solc: yes 11 | runtime: 12 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$BIN'" 13 | -------------------------------------------------------------------------------- /tools/securify/findings.yaml: -------------------------------------------------------------------------------- 1 | DAO: 2 | classification: SWC-107, DASP-1 3 | DAOConstantGas: 4 | classification: SWC-107, DASP-1 5 | MissingInputValidation: 6 | classification: DASP-3 7 | TODAmount: 8 | classification: SWC-114, DASP-7 9 | TODReceiver: 10 | classification: SWC-114, DASP-7 11 | TODTransfer: 12 | classification: SWC-114, DASP-7 13 | UnhandledException: 14 | classification: SWC-104, DASP-4 15 | UnrestrictedEtherFlow: 16 | classification: SWC-105, DASP-2 17 | -------------------------------------------------------------------------------- /tools/securify/parser.py: -------------------------------------------------------------------------------- 1 | import json, io, tarfile 2 | import sb.parse_utils 3 | 4 | VERSION = "2022/11/17" 5 | 6 | FINDINGS = { 7 | "DAO", 8 | "DAOConstantGas", 9 | "MissingInputValidation", 10 | "TODAmount", 11 | "TODReceiver", 12 | "TODTransfer", 13 | "UnhandledException", 14 | "UnrestrictedEtherFlow" 15 | } 16 | 17 | def parse(exit_code, log, output): 18 | findings, infos = set(), set() 19 | errors, fails = sb.parse_utils.errors_fails(exit_code, log, log_expected=False) 20 | if fails: 21 | errors.discard("EXIT_CODE_1") 22 | 23 | # We look for the output in the following places, in that order: 24 | # - log (=stdout+stderr) 25 | # - output:results/results.json 26 | # - output:results/live.json 27 | try: 28 | try: 29 | analysis = json.loads(log) 30 | except: 31 | with io.BytesIO(output) as o, tarfile.open(fileobj=o) as tar: 32 | try: 33 | jsn = tar.extractfile("results/results.json").read() 34 | analysis = json.loads(jsn) 35 | except: 36 | jsn = tar.extractfile("results/live.json").read() 37 | analysis = json.loads(jsn) 38 | except Exception: 39 | analysis = {} 40 | 41 | if not analysis: 42 | infos.add("analysis incomplete") 43 | elif "patternResults" in analysis: # live.json 44 | if "finished" in analysis and not analysis["finished"]: 45 | infos.add("analysis incomplete") 46 | if "decompiled" in analysis and not analysis["decompiled"]: 47 | errors.add("decompilation error") 48 | for vuln,check in analysis["patternResults"].items(): 49 | if not check["completed"]: 50 | infos.add("analysis incomplete") 51 | if check["hasViolations"]: 52 | findings.add(vuln) 53 | else: # log or result.json 54 | for contract,analysis in analysis.items(): 55 | for vuln,check in analysis["results"].items(): 56 | if check["violations"]: 57 | findings.add(vuln) 58 | 59 | if "analysis incomplete" in infos and not fails: 60 | fails.add("execution failed") 61 | 62 | findings = [ { "name": vuln } for vuln in findings ] 63 | 64 | return findings, infos, errors, fails 65 | 66 | -------------------------------------------------------------------------------- /tools/securify/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | 6 | mkdir /results 7 | java -Xmx16G -jar /securify_jar/securify.jar --livestatusfile /results/live.json --output /results/results.json -fh "$FILENAME" 8 | -------------------------------------------------------------------------------- /tools/securify/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | 6 | export PATH="$BIN:$PATH" 7 | chmod +x "$BIN/solc" 8 | 9 | mkdir /results 10 | java -Xmx16G -jar /securify_jar/securify.jar --livestatusfile /results/live.json --output /results/results.json -fs "$FILENAME" 11 | -------------------------------------------------------------------------------- /tools/semgrep/Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM returntocorp/semgrep:sha-c3a9f40 2 | LABEL maintainer="raphinaliu@gmail.com" 3 | WORKDIR /semgrep-smart-contracts 4 | RUN git clone https://github.com/Decurity/semgrep-smart-contracts.git . 5 | 6 | -------------------------------------------------------------------------------- /tools/semgrep/config.yaml: -------------------------------------------------------------------------------- 1 | name: semgrep 2 | version: c3a9f40 3 | origin: https://semgrep.dev/ 4 | info: Find patterns of vulnerabilities in smart contracts based on actual DeFi exploits as well as gas optimization rules that can be used as a part of the CI pipeline. 5 | image: smartbugs/semgrep:c3a9f40 6 | bin: scripts 7 | solidity: 8 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 9 | solc: yes 10 | bin: scripts 11 | 12 | 13 | -------------------------------------------------------------------------------- /tools/semgrep/findings.yaml: -------------------------------------------------------------------------------- 1 | unnecessary-checked-arithmetic-in-loop:{} 2 | proxy-storage-collision: 3 | classification: SWC-124, DASP-2 4 | erc20-public-burn:{} 5 | non-payable-constructor:{} 6 | use-prefix-increment-not-postfix:{} 7 | balancer-readonly-reentrancy-getrate: 8 | classification: SWC-107, DASP-1 9 | erc777-reentrancy: 10 | classification: SWC-107, DASP-1 11 | use-short-revert-string:{} 12 | init-variables-with-default-value:{} 13 | unrestricted-transferownership: 14 | classification: , DASP-2 15 | erc20-public-transfer:{} 16 | openzeppelin-ecdsa-recover-malleable: 17 | classification: SWC-117 18 | erc721-reentrancy: 19 | classification: SWC-107, DASP-1 20 | erc677-reentrancy: 21 | classification: SWC-107, DASP-1 22 | array-length-outside-loop:{} 23 | no-slippage-check:{} 24 | basic-arithmetic-underflow: 25 | classification: SWC-101, DASP-3 26 | oracle-price-update-not-restricted: 27 | classification: DASP-2 28 | use-multiple-require:{} 29 | compound-borrowfresh-reentrancy: 30 | classification: SWC-107, DASP-1 31 | use-prefix-decrement-not-postfix:{} 32 | use-nested-if:{} 33 | gearbox-tokens-path-confusion:{} 34 | redacted-cartel-custom-approval-bug:{} 35 | erc721-arbitrary-transferfrom:{} 36 | balancer-readonly-reentrancy-getpooltokens: 37 | classification: SWC-107, DASP-1 38 | incorrect-use-of-blockhash:{} 39 | curve-readonly-reentrancy: 40 | classification: SWC-107, DASP-1 41 | sense-missing-oracle-access-control: 42 | classification: DASP-2 43 | encode-packed-collision: 44 | classification: SWC-133, DASP-1 45 | uniswap-callback-not-protected: 46 | classification: DASP-2 47 | keeper-network-oracle-manipulation: 48 | classification: SWC-101, DASP-3 49 | tecra-coin-burnfrom-bug:{} 50 | use-ownable2step:{} 51 | state-variable-read-in-a-loop:{} 52 | use-abi-encodecall-instead-of-encodewithselector:{} 53 | delegatecall-to-arbitrary-address: 54 | classification: SWC-112, DASP-10 55 | arbitrary-low-level-call: 56 | classification: SWC-112, DASP-10 57 | 58 | superfluid-ctx-injection:{} 59 | non-optimal-variables-swap:{} 60 | use-custom-error-not-require:{} 61 | inefficient-state-variable-increment:{} 62 | compound-sweeptoken-not-restricted: 63 | classification: SWC-105, DASP-2 64 | accessible-selfdestruct: 65 | classification: SWC-106, DASP-2 66 | no-bidi-characters: 67 | classification: SWC-130 68 | basic-oracle-manipulation: 69 | classification: SWC-101, DASP-3 70 | msg-value-multicall:{} 71 | rigoblock-missing-access-control: 72 | classification: DASP-2 -------------------------------------------------------------------------------- /tools/semgrep/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x "$BIN/solc" 9 | 10 | semgrep --config ./solidity "$FILENAME" 11 | -------------------------------------------------------------------------------- /tools/sfuzz/config.yaml: -------------------------------------------------------------------------------- 1 | name: sFuzz 2 | version: #48934c0 (2019-03-01) 3 | info: An Efficient Adaptive Fuzzer for Solidity Smart Contracts 4 | origin: https://github.com/duytai/sFuzz 5 | image: smartbugs/sfuzz:48934c0 6 | 7 | solidity: 8 | command: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN' '$MAIN'" 9 | output: /home/contracts 10 | bin: scripts 11 | solc: yes 12 | -------------------------------------------------------------------------------- /tools/sfuzz/findings.yaml: -------------------------------------------------------------------------------- 1 | Block Number Dependency: 2 | classification: SWC-120, DASP-6 3 | severity: Low 4 | 5 | Dangerous Delegate Call: 6 | classification: SWC-106, DASP-2 7 | 8 | Exception Disorder: 9 | classification: SWC-104, DASP-4 10 | 11 | Freezing Ether: 12 | classification: DASP-5 13 | 14 | Gasless Send: 15 | classification: SWC-134 16 | 17 | Integer Overflow: 18 | classification: SWC-101, DASP-3 19 | severity: High 20 | 21 | Integer Underflow: 22 | classification: SWC-101, DASP-3 23 | severity: High 24 | 25 | Reentrancy: 26 | classification: SWC-107, DASP-1 27 | severity: High 28 | 29 | Timestamp Dependency: 30 | classification: SWC-116, DASP-8 31 | -------------------------------------------------------------------------------- /tools/sfuzz/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | MAIN="$4" 7 | 8 | export PATH="$BIN:$PATH" 9 | chmod +x "$BIN/solc" 10 | 11 | CONTRACT="${FILENAME%.sol}" 12 | CONTRACT="${CONTRACT##*/}" 13 | CONTRACTS=$(python3 "$BIN"/printContractNames.py "$FILENAME") 14 | COUNT=$(echo $CONTRACTS | wc -w) 15 | [ "$COUNT" -gt 0 ] || COUNT=1 16 | 17 | if [ "$MAIN" -eq 1 ]; then 18 | if (echo "$CONTRACTS" | grep -q "$CONTRACT"); then 19 | CONTRACTS="$CONTRACT" 20 | COUNT=1 21 | else 22 | echo "Contract '$CONTRACT' not found in $FILENAME" 23 | exit 127 24 | fi 25 | fi 26 | 27 | mkdir -p /home/contracts 28 | 29 | for CONTRACT in $CONTRACTS; do 30 | echo "Extract contract $CONTRACT from $FILENAME" 31 | cp "$FILENAME" "/home/contracts/$CONTRACT.sol" 32 | done 33 | 34 | echo "Extracted $COUNT contract(s) from $FILENAME" 35 | 36 | # Fuzz each contract at least 10 seconds 37 | TO=$(((TIMEOUT - (2 * COUNT)) / COUNT)) 38 | if [ "$TIMEOUT" -eq 0 ] || [ $TO -lt 10 ]; then 39 | TO=120 40 | fi 41 | 42 | cd /home/ && ./fuzzer -g -r 1 -d $TO && chmod +x fuzzMe && ./fuzzMe 43 | -------------------------------------------------------------------------------- /tools/sfuzz/scripts/printContractNames.py: -------------------------------------------------------------------------------- 1 | import sys, json 2 | from subprocess import PIPE, Popen 3 | 4 | filename = sys.argv[1] 5 | cmd = ["solc", "--standard-json", "--allow-paths", ".,/"] 6 | settings = { 7 | "optimizer": {"enabled": False}, 8 | "outputSelection": { 9 | "*": { 10 | "*": [ "evm.deployedBytecode" ], 11 | } 12 | }, 13 | } 14 | 15 | input_json = json.dumps( 16 | { 17 | "language": "Solidity", 18 | "sources": {filename: {"urls": [filename]}}, 19 | "settings": settings, 20 | } 21 | ) 22 | p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 23 | stdout, stderr = p.communicate(bytes(input_json, "utf8")) 24 | out = stdout.decode("UTF-8") 25 | result = json.loads(out) 26 | for error in result.get("errors", []): 27 | if error["severity"] == "error": 28 | print(error["formattedMessage"]) 29 | sys.exit(1) 30 | contracts = result["contracts"][filename] 31 | for contract in contracts.keys(): 32 | if len(contracts[contract]["evm"]["deployedBytecode"]["object"]): 33 | print(contract) 34 | -------------------------------------------------------------------------------- /tools/slither-0.10.0/config.yaml: -------------------------------------------------------------------------------- 1 | name: Slither 2 | version: 0.10.0 3 | origin: https://github.com/crytic/slither 4 | info: Slither is a Solidity static analysis framework written in Python 3. It runs a suite of vulnerability detectors and prints visual information about contract details. Slither enables developers to find vulnerabilities, enhance their code comphrehension, and quickly prototype custom analyses. 5 | image: smartbugs/slither:0.10.0 6 | output: /output.json 7 | bin: scripts 8 | solidity: 9 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 10 | solc: yes 11 | -------------------------------------------------------------------------------- /tools/slither-0.10.0/findings.yaml: -------------------------------------------------------------------------------- 1 | abiencoderv2-array: {} 2 | arbitrary-send-erc20: {} 3 | arbitrary-send-erc20-permit: {} 4 | arbitrary-send-eth: 5 | classification: SWC-105, DASP-2 6 | array-by-reference: {} 7 | assembly: {} 8 | assert-state-change: {} 9 | backdoor: {} 10 | boolean-cst: {} 11 | boolean-equal: {} 12 | cache-array-length: {} 13 | calls-loop: 14 | classification: SWC-113, DASP-5 15 | codex: {} 16 | constable-states: {} 17 | constant-function-asm: {} 18 | constant-function-state: {} 19 | controlled-array-length: {} 20 | controlled-delegatecall: 21 | classification: SWC-106, DASP-2 22 | costly-loop: {} 23 | cyclomatic-complexity: {} 24 | dead-code: {} 25 | delegatecall-loop: {} 26 | deprecated-standards: {} 27 | divide-before-multiply: {} 28 | domain-separator-collision: {} 29 | encode-packed-collision: {} 30 | enum-conversion: {} 31 | erc20-indexed: {} 32 | erc20-interface: {} 33 | erc721-interface: {} 34 | events-access: {} 35 | events-maths: {} 36 | external-function: {} 37 | function-init-state: {} 38 | immutable-states: {} 39 | incorrect-equality: {} 40 | incorrect-exp: {} 41 | incorrect-modifier: {} 42 | incorrect-return: {} 43 | incorrect-shift: {} 44 | incorrect-unary: {} 45 | incorrect-using-for: {} 46 | locked-ether: 47 | classification: DASP-5 48 | low-level-calls: 49 | classification: SWC-104, DASP-4 50 | mapping-deletion: {} 51 | missing-inheritance: {} 52 | missing-zero-check: {} 53 | msg-value-loop: {} 54 | multiple-constructors: {} 55 | name-reused: {} 56 | naming-convention: {} 57 | out-of-order-retryable: {} 58 | pragma: 59 | classification: SWC-103 60 | protected-vars: {} 61 | public-mappings-nested: {} 62 | redundant-statements: {} 63 | reentrancy-benign: 64 | classification: SWC-107, DASP-1 65 | reentrancy-eth: 66 | classification: SWC-107, DASP-1 67 | reentrancy-events: {} 68 | reentrancy-no-eth: 69 | classification: SWC-107, DASP-1 70 | reentrancy-unlimited-gas: {} 71 | return-bomb: {} 72 | return-leave: {} 73 | reused-constructor: {} 74 | rtlo: {} 75 | shadowing-abstract: 76 | classification: SWC-119 77 | shadowing-builtin: {} 78 | shadowing-local: 79 | classification: SWC-119 80 | shadowing-state: 81 | classification: SWC-119 82 | similar-names: {} 83 | solc-version: 84 | classification: SWC-102 85 | storage-array: {} 86 | suicidal: 87 | classification: SWC-106, DASP-2 88 | tautological-compare: {} 89 | tautology: {} 90 | timestamp: 91 | classification: SWC-116, DASP-8 92 | token-reentrancy: {} 93 | too-many-digits: {} 94 | tx-origin: 95 | classification: SWC-115, DASP-2 96 | unchecked-lowlevel: {} 97 | unchecked-send: {} 98 | unchecked-transfer: {} 99 | unimplemented-functions: {} 100 | uninitialized-fptr-cst: {} 101 | uninitialized-local: {} 102 | uninitialized-state: 103 | classification: SWC-109 104 | uninitialized-storage: 105 | classification: SWC-109 106 | unprotected-upgrade: {} 107 | unused-import: {} 108 | unused-return: 109 | classification: SWC-104, DASP-4 110 | unused-state: 111 | classification: SWC-131 112 | variable-scope: {} 113 | var-read-using-this: {} 114 | void-cst: {} 115 | weak-prng: {} 116 | write-after-write: {} 117 | -------------------------------------------------------------------------------- /tools/slither-0.10.0/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x "$BIN/solc" 9 | 10 | slither "$FILENAME" --json /output.json 11 | -------------------------------------------------------------------------------- /tools/slither-0.10.4/config.yaml: -------------------------------------------------------------------------------- 1 | name: Slither 2 | version: 0.10.4 3 | origin: https://github.com/crytic/slither 4 | info: Slither is a Solidity static analysis framework written in Python 3. It runs a suite of vulnerability detectors and prints visual information about contract details. Slither enables developers to find vulnerabilities, enhance their code comphrehension, and quickly prototype custom analyses. 5 | image: smartbugs/slither:0.10.4 6 | output: /output.json 7 | bin: scripts 8 | solidity: 9 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 10 | solc: yes 11 | -------------------------------------------------------------------------------- /tools/slither-0.10.4/findings.yaml: -------------------------------------------------------------------------------- 1 | abiencoderv2-array: {} 2 | arbitrary-send-erc20: {} 3 | arbitrary-send-erc20-permit: {} 4 | arbitrary-send-eth: 5 | classification: SWC-105, DASP-2 6 | array-by-reference: {} 7 | assembly: {} 8 | assert-state-change: {} 9 | backdoor: {} 10 | boolean-cst: {} 11 | boolean-equal: {} 12 | cache-array-length: {} 13 | calls-loop: 14 | classification: SWC-113, DASP-5 15 | codex: {} 16 | constable-states: {} 17 | constant-function-asm: {} 18 | constant-function-state: {} 19 | controlled-array-length: {} 20 | controlled-delegatecall: 21 | classification: SWC-106, DASP-2 22 | costly-loop: {} 23 | cyclomatic-complexity: {} 24 | dead-code: {} 25 | delegatecall-loop: {} 26 | deprecated-standards: {} 27 | divide-before-multiply: {} 28 | domain-separator-collision: {} 29 | encode-packed-collision: {} 30 | enum-conversion: {} 31 | erc20-indexed: {} 32 | erc20-interface: {} 33 | erc721-interface: {} 34 | events-access: {} 35 | events-maths: {} 36 | external-function: {} 37 | function-init-state: {} 38 | immutable-states: {} 39 | incorrect-equality: {} 40 | incorrect-exp: {} 41 | incorrect-modifier: {} 42 | incorrect-return: {} 43 | incorrect-shift: {} 44 | incorrect-unary: {} 45 | incorrect-using-for: {} 46 | locked-ether: 47 | classification: DASP-5 48 | low-level-calls: 49 | classification: SWC-104, DASP-4 50 | mapping-deletion: {} 51 | missing-inheritance: {} 52 | missing-zero-check: {} 53 | msg-value-loop: {} 54 | multiple-constructors: {} 55 | name-reused: {} 56 | naming-convention: {} 57 | out-of-order-retryable: {} 58 | pragma: 59 | classification: SWC-103 60 | protected-vars: {} 61 | public-mappings-nested: {} 62 | redundant-statements: {} 63 | reentrancy-benign: 64 | classification: SWC-107, DASP-1 65 | reentrancy-eth: 66 | classification: SWC-107, DASP-1 67 | reentrancy-events: {} 68 | reentrancy-no-eth: 69 | classification: SWC-107, DASP-1 70 | reentrancy-unlimited-gas: {} 71 | return-bomb: {} 72 | return-leave: {} 73 | reused-constructor: {} 74 | rtlo: {} 75 | shadowing-abstract: 76 | classification: SWC-119 77 | shadowing-builtin: {} 78 | shadowing-local: 79 | classification: SWC-119 80 | shadowing-state: 81 | classification: SWC-119 82 | similar-names: {} 83 | solc-version: 84 | classification: SWC-102 85 | storage-array: {} 86 | suicidal: 87 | classification: SWC-106, DASP-2 88 | tautological-compare: {} 89 | tautology: {} 90 | timestamp: 91 | classification: SWC-116, DASP-8 92 | token-reentrancy: {} 93 | too-many-digits: {} 94 | tx-origin: 95 | classification: SWC-115, DASP-2 96 | unchecked-lowlevel: {} 97 | unchecked-send: {} 98 | unchecked-transfer: {} 99 | unimplemented-functions: {} 100 | uninitialized-fptr-cst: {} 101 | uninitialized-local: {} 102 | uninitialized-state: 103 | classification: SWC-109 104 | uninitialized-storage: 105 | classification: SWC-109 106 | unprotected-upgrade: {} 107 | unused-import: {} 108 | unused-return: 109 | classification: SWC-104, DASP-4 110 | unused-state: 111 | classification: SWC-131 112 | variable-scope: {} 113 | var-read-using-this: {} 114 | void-cst: {} 115 | weak-prng: {} 116 | write-after-write: {} 117 | -------------------------------------------------------------------------------- /tools/slither-0.10.4/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x "$BIN/solc" 9 | 10 | slither "$FILENAME" --json /output.json 11 | -------------------------------------------------------------------------------- /tools/slither-0.6.1/config.yaml: -------------------------------------------------------------------------------- 1 | name: Slither 2 | version: 0.6.1 3 | origin: https://github.com/crytic/slither 4 | info: Slither is a Solidity static analysis framework written in Python 3. It runs a suite of vulnerability detectors and prints visual information about contract details. Slither enables developers to find vulnerabilities, enhance their code comphrehension, and quickly prototype custom analyses. 5 | image: smartbugs/slither:0.6.1 6 | output: /output.json 7 | bin: scripts 8 | solidity: 9 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 10 | solc: yes 11 | -------------------------------------------------------------------------------- /tools/slither-0.6.1/findings.yaml: -------------------------------------------------------------------------------- 1 | abiencoderv2-array: {} 2 | arbitrary-send: 3 | classification: SWC-105, DASP-2 4 | arbitrary-send-erc20: {} 5 | arbitrary-send-erc20-permit: {} 6 | arbitrary-send-eth: {} 7 | array-by-reference: {} 8 | assembly: {} 9 | assert-state-change: {} 10 | backdoor: {} 11 | boolean-cst: {} 12 | boolean-equal: {} 13 | calls-loop: 14 | classification: SWC-113, DASP-5 15 | complex-function: {} 16 | constable-states: {} 17 | constant-function: {} 18 | constant-function-asm: {} 19 | constant-function-state: {} 20 | controlled-array-length: {} 21 | controlled-delegatecall: 22 | classification: SWC-106, DASP-2 23 | costly-loop: {} 24 | dead-code: {} 25 | delegatecall-loop: {} 26 | deprecated-standards: {} 27 | divide-before-multiply: {} 28 | domain-separator-collision: {} 29 | enum-conversion: {} 30 | erc20-indexed: {} 31 | erc20-interface: {} 32 | erc721-interface: {} 33 | events-access: {} 34 | events-maths: {} 35 | external-function: {} 36 | function-init-state: {} 37 | incorrect-equality: {} 38 | incorrect-modifier: {} 39 | incorrect-shift: {} 40 | incorrect-unary: {} 41 | locked-ether: 42 | classification: DASP-5 43 | low-level-calls: 44 | classification: SWC-104, DASP-4 45 | mapping-deletion: {} 46 | missing-inheritance: {} 47 | missing-zero-check: {} 48 | msg-value-loop: {} 49 | multiple-constructors: {} 50 | name-reused: {} 51 | naming-convention: {} 52 | pragma: 53 | classification: SWC-103 54 | protected-vars: {} 55 | public-mappings-nested: {} 56 | redundant-statements: {} 57 | reentrancy-benign: 58 | classification: SWC-107, DASP-1 59 | reentrancy-eth: 60 | classification: SWC-107, DASP-1 61 | reentrancy-events: {} 62 | reentrancy-no-eth: 63 | classification: SWC-107, DASP-1 64 | reentrancy-unlimited-gas: {} 65 | reused-constructor: {} 66 | rtlo: {} 67 | shadowing-abstract: 68 | classification: SWC-119 69 | shadowing-builtin: {} 70 | shadowing-local: 71 | classification: SWC-119 72 | shadowing-state: 73 | classification: SWC-119 74 | similar-names: {} 75 | solc-version: 76 | classification: SWC-102 77 | storage-array: {} 78 | suicidal: 79 | classification: SWC-106, DASP-2 80 | tautology: {} 81 | timestamp: 82 | classification: SWC-116, DASP-8 83 | token-reentrancy: {} 84 | too-many-digits: {} 85 | tx-origin: 86 | classification: SWC-115, DASP-2 87 | unchecked-lowlevel: {} 88 | unchecked-send: {} 89 | unchecked-transfer: {} 90 | unimplemented-functions: {} 91 | uninitialized-fptr-cst: {} 92 | uninitialized-local: {} 93 | uninitialized-state: 94 | classification: SWC-109 95 | uninitialized-storage: 96 | classification: SWC-109 97 | unprotected-upgrade: {} 98 | unused-return: 99 | classification: SWC-104, DASP-4 100 | unused-state: 101 | classification: SWC-131 102 | variable-scope: {} 103 | void-cst: {} 104 | weak-prng: {} 105 | write-after-write: {} 106 | -------------------------------------------------------------------------------- /tools/slither-0.6.1/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x "$BIN/solc" 9 | 10 | slither "$FILENAME" --json /output.json 11 | -------------------------------------------------------------------------------- /tools/slither/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - slither-0.10.4 3 | -------------------------------------------------------------------------------- /tools/smartcheck/config.yaml: -------------------------------------------------------------------------------- 1 | name: Smartcheck 2 | origin: https://github.com/smartdec/smartcheck 3 | info: SmartCheck is an extensible static analysis tool for discovering vulnerabilities and other code issues in Ethereum smart contracts written in the Solidity programming language. 4 | solidity: 5 | image: smartbugs/smartcheck 6 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN'" 7 | solc: yes 8 | bin: scripts 9 | -------------------------------------------------------------------------------- /tools/smartcheck/findings.yaml: -------------------------------------------------------------------------------- 1 | SOLIDITY_ADDRESS_HARDCODED: {} 2 | SOLIDITY_ARRAY_LENGTH_MANIPULATION: {} 3 | SOLIDITY_BALANCE_EQUALITY: 4 | classification: SWC-132 5 | SOLIDITY_BYTE_ARRAY_INSTEAD_BYTES: {} 6 | SOLIDITY_CALL_WITHOUT_DATA: {} 7 | SOLIDITY_CONSTRUCTOR_RETURN: {} 8 | SOLIDITY_DELETE_ON_DYNAMIC_ARRAYS: {} 9 | SOLIDITY_DEPRECATED_CONSTRUCTIONS: 10 | classification: SWC-111 11 | SOLIDITY_DIV_MUL: {} 12 | SOLIDITY_DOS_WITH_THROW: {} 13 | SOLIDITY_DO_WHILE_CONTINUE: {} 14 | SOLIDITY_ERC20_APPROVE: {} 15 | SOLIDITY_ERC20_FUNCTIONS_ALWAYS_RETURN_FALSE: {} 16 | SOLIDITY_ERC20_INDEXED: {} 17 | SOLIDITY_ERC20_TRANSFER_SHOULD_THROW: {} 18 | SOLIDITY_EXACT_TIME: 19 | classification: SWC-116, DASP-8 20 | SOLIDITY_EXTRA_GAS_IN_LOOPS: {} 21 | SOLIDITY_FUNCTIONS_RETURNS_TYPE_AND_NO_RETURN: {} 22 | SOLIDITY_FUNCTION_RETURNS_TYPE_AND_NO_RETURN: {} 23 | SOLIDITY_GAS_LIMIT_IN_LOOPS: 24 | classification: SWC-132 25 | SOLIDITY_INCORRECT_BLOCKHASH: {} 26 | SOLIDITY_LOCKED_MONEY: 27 | classification: DASP-5 28 | SOLIDITY_MSGVALUE_EQUALS_ZERO: {} 29 | SOLIDITY_OVERPOWERED_ROLE: {} 30 | SOLIDITY_PRAGMAS_VERSION: 31 | classification: SWC-103 32 | SOLIDITY_PRIVATE_MODIFIER_DOES_NOT_HIDE_DATA: {} 33 | SOLIDITY_PRIVATE_MODIFIER_DONT_HIDE_DATA: {} 34 | SOLIDITY_REDUNDANT_FALLBACK_REJECT: {} 35 | SOLIDITY_REVERT_REQUIRE: {} 36 | SOLIDITY_REWRITE_ON_ASSEMBLY_CALL: {} 37 | SOLIDITY_SAFEMATH: {} 38 | SOLIDITY_SEND: {} 39 | SOLIDITY_SHOULD_NOT_BE_PURE: {} 40 | SOLIDITY_SHOULD_NOT_BE_VIEW: {} 41 | SOLIDITY_SHOULD_RETURN_STRUCT: {} 42 | SOLIDITY_TRANSFER_IN_LOOP: 43 | classification: SWC-113, DASP-5 44 | SOLIDITY_TX_ORIGIN: 45 | classification: SWC-115, DASP-2 46 | SOLIDITY_UINT_CANT_BE_NEGATIVE: {} 47 | SOLIDITY_UNCHECKED_CALL: 48 | classification: SWC-104, DASP-4 49 | SOLIDITY_UNUSED_FUNCTION_SHOULD_BE_EXTERNAL: {} 50 | SOLIDITY_UPGRADE_TO_050: {} 51 | SOLIDITY_USING_INLINE_ASSEMBLY: {} 52 | SOLIDITY_VAR: 53 | classification: SWC-101, DASP-3 54 | SOLIDITY_VAR_IN_LOOP_FOR: {} 55 | SOLIDITY_VISIBILITY: 56 | classification: SWC-100 57 | SOLIDITY_WRONG_SIGNATURE: {} 58 | -------------------------------------------------------------------------------- /tools/smartcheck/parser.py: -------------------------------------------------------------------------------- 1 | import sb.parse_utils 2 | 3 | VERSION = "2022/11/14" 4 | 5 | FINDINGS = { 6 | "SOLIDITY_ADDRESS_HARDCODED", 7 | "SOLIDITY_ARRAY_LENGTH_MANIPULATION", 8 | "SOLIDITY_BALANCE_EQUALITY", 9 | "SOLIDITY_BYTE_ARRAY_INSTEAD_BYTES", 10 | "SOLIDITY_CONSTRUCTOR_RETURN", 11 | "SOLIDITY_CALL_WITHOUT_DATA", 12 | "SOLIDITY_DELETE_ON_DYNAMIC_ARRAYS", 13 | "SOLIDITY_DEPRECATED_CONSTRUCTIONS", 14 | "SOLIDITY_DIV_MUL", 15 | "SOLIDITY_DO_WHILE_CONTINUE", 16 | "SOLIDITY_DOS_WITH_THROW", 17 | "SOLIDITY_ERC20_APPROVE", 18 | "SOLIDITY_ERC20_FUNCTIONS_ALWAYS_RETURN_FALSE", 19 | "SOLIDITY_ERC20_INDEXED", 20 | "SOLIDITY_ERC20_TRANSFER_SHOULD_THROW", 21 | "SOLIDITY_EXACT_TIME", 22 | "SOLIDITY_EXTRA_GAS_IN_LOOPS", 23 | "SOLIDITY_FUNCTION_RETURNS_TYPE_AND_NO_RETURN", 24 | "SOLIDITY_FUNCTIONS_RETURNS_TYPE_AND_NO_RETURN", 25 | "SOLIDITY_GAS_LIMIT_IN_LOOPS", 26 | "SOLIDITY_INCORRECT_BLOCKHASH", 27 | "SOLIDITY_LOCKED_MONEY", 28 | "SOLIDITY_MSGVALUE_EQUALS_ZERO", 29 | "SOLIDITY_OVERPOWERED_ROLE", 30 | "SOLIDITY_PRAGMAS_VERSION", 31 | "SOLIDITY_PRIVATE_MODIFIER_DOES_NOT_HIDE_DATA", 32 | "SOLIDITY_PRIVATE_MODIFIER_DONT_HIDE_DATA", 33 | "SOLIDITY_REDUNDANT_FALLBACK_REJECT", 34 | "SOLIDITY_REVERT_REQUIRE", 35 | "SOLIDITY_REWRITE_ON_ASSEMBLY_CALL", 36 | "SOLIDITY_SAFEMATH", 37 | "SOLIDITY_SEND", 38 | "SOLIDITY_SHOULD_NOT_BE_PURE", 39 | "SOLIDITY_SHOULD_NOT_BE_VIEW", 40 | "SOLIDITY_SHOULD_RETURN_STRUCT", 41 | "SOLIDITY_TRANSFER_IN_LOOP", 42 | "SOLIDITY_TX_ORIGIN", 43 | "SOLIDITY_UINT_CANT_BE_NEGATIVE", 44 | "SOLIDITY_UNCHECKED_CALL", 45 | "SOLIDITY_UNUSED_FUNCTION_SHOULD_BE_EXTERNAL", 46 | "SOLIDITY_UPGRADE_TO_050", 47 | "SOLIDITY_USING_INLINE_ASSEMBLY", 48 | "SOLIDITY_VAR", 49 | "SOLIDITY_VAR_IN_LOOP_FOR", 50 | "SOLIDITY_VISIBILITY", 51 | "SOLIDITY_WRONG_SIGNATURE", 52 | } 53 | 54 | def parse(exit_code, log, output): 55 | findings, infos = [], set() 56 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 57 | 58 | for line in log: 59 | i = line.find(": ") 60 | if i >= 0: 61 | k = line[0:i].strip() 62 | v = line[i+2:].strip() 63 | if v.isdigit(): 64 | v = int(v) 65 | if k.endswith("ruleId"): 66 | finding = { "name": v } 67 | findings.append(finding) 68 | elif k in ("severity", "line", "column"): 69 | finding[k] = v 70 | 71 | return findings, infos, errors, fails 72 | -------------------------------------------------------------------------------- /tools/smartcheck/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | 6 | export PATH="$BIN:$PATH" 7 | chmod +x "$BIN/solc" 8 | 9 | smartcheck -p "$FILENAME" 10 | -------------------------------------------------------------------------------- /tools/solhint-2.1.0/config.yaml: -------------------------------------------------------------------------------- 1 | name: Solhint 2 | version: "2.1.0" 3 | origin: https://github.com/protofire/solhint 4 | info: Open source project for linting solidity code. This project provide both security and style guide validations. 5 | image: smartbugs/solhint:2.1.0 6 | solidity: 7 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 8 | solc: yes 9 | bin: scripts 10 | -------------------------------------------------------------------------------- /tools/solhint-2.1.0/parser.py: -------------------------------------------------------------------------------- 1 | import sb.parse_utils 2 | 3 | VERSION = "2022/11/14" 4 | 5 | FINDINGS = { 6 | "array-declaration-spaces", 7 | "avoid-call-value", 8 | "avoid-low-level-calls", 9 | "avoid-sha3", 10 | "avoid-suicide", 11 | "avoid-throw", 12 | "avoid-tx-origin", 13 | "bracket-align", 14 | "check-send-result", 15 | "code-complexity", 16 | "compiler-fixed", 17 | "compiler-gt-0_4", 18 | "compiler-version", 19 | "comprehensive-interface", 20 | "const-name-snakecase", 21 | "constructor-syntax", 22 | "contract-name-camelcase", 23 | "event-name-camelcase", 24 | "expression-indent", 25 | "func-name-mixedcase", 26 | "func-order", 27 | "func-param-name-mixedcase", 28 | "function-max-lines", 29 | "func-visibility", 30 | "imports-on-top", 31 | "indent", 32 | "mark-callable-contracts", 33 | "max-line-length", 34 | "max-states-count", 35 | "modifier-name-mixedcase", 36 | "multiple-sends", 37 | "no-complex-fallback", 38 | "no-empty-blocks", 39 | "no-inline-assembly", 40 | "no-mix-tabs-and-spaces", 41 | "no-simple-event-func-name", 42 | "no-spaces-before-semicolon", 43 | "not-rely-on-block-hash", 44 | "not-rely-on-time", 45 | "no-unused-vars", 46 | "ordering", 47 | "payable-fallback", 48 | "private-vars-leading-underscore", 49 | "quotes", 50 | "reason-string", 51 | "reentrancy", 52 | "separate-by-one-line-in-contract", 53 | "space-after-comma", 54 | "statement-indent", 55 | "state-visibility", 56 | "two-lines-top-level-separator", 57 | "use-forbidden-name", 58 | "var-name-mixedcase", 59 | "visibility-modifier-order", 60 | } 61 | 62 | def parse(exit_code, log, output): 63 | findings, infos = [], set() 64 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 65 | 66 | for line in log: 67 | if ":" in line: 68 | s_result = line.split(":") 69 | if len(s_result) != 4: 70 | continue 71 | (file, lineno, column, end_error) = s_result 72 | if "]" not in end_error: 73 | continue 74 | message = end_error[1:end_error.index("[") - 1] 75 | level = end_error[end_error.index("[") + 1: end_error.index("/")] 76 | name = end_error[end_error.index("/") + 1: len(end_error) - 1] 77 | findings.append({ 78 | "filename": file, 79 | "line": int(lineno), 80 | "column": int(column), 81 | "message": message, 82 | "level": level.lower(), 83 | "name": name 84 | }) 85 | 86 | return findings, infos, errors, fails 87 | -------------------------------------------------------------------------------- /tools/solhint-2.1.0/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | export PATH="$BIN:$PATH" 8 | chmod +x "$BIN/solc" 9 | 10 | solhint -f unix -q "$FILENAME" 11 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/config.yaml: -------------------------------------------------------------------------------- 1 | name: Solhint 2 | version: "3.3.8" 3 | origin: https://github.com/protofire/solhint 4 | info: Open source project for linting solidity code. This project provide both security and style guide validations. 5 | image: smartbugs/solhint:3.3.8 6 | solidity: 7 | entrypoint: "'$BIN/do_solidity.sh' '$FILENAME' '$BIN'" 8 | solc: yes 9 | bin: scripts 10 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/docker/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["solhint:recommended"] 3 | } 4 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | RUN npm install -g solhint 3 | COPY docker-entrypoint.sh /usr/local/bin 4 | COPY .solhint.json / 5 | ENTRYPOINT [ "docker-entrypoint.sh" ] 6 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ]; then 5 | set -- node "$@" 6 | fi 7 | 8 | exec "$@" 9 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/parser.py: -------------------------------------------------------------------------------- 1 | import sb.parse_utils 2 | 3 | VERSION = "2023/02/12" 4 | 5 | FINDINGS = { 6 | "array-declaration-spaces", 7 | "avoid-call-value", 8 | "avoid-low-level-calls", 9 | "avoid-sha3", 10 | "avoid-suicide", 11 | "avoid-throw", 12 | "avoid-tx-origin", 13 | "bracket-align", 14 | "check-send-result", 15 | "code-complexity", 16 | "compiler-fixed", 17 | "compiler-gt-0_4", 18 | "compiler-version", 19 | "comprehensive-interface", 20 | "const-name-snakecase", 21 | "constructor-syntax", 22 | "contract-name-camelcase", 23 | "event-name-camelcase", 24 | "expression-indent", 25 | "func-name-mixedcase", 26 | "func-order", 27 | "func-param-name-mixedcase", 28 | "function-max-lines", 29 | "func-visibility", 30 | "imports-on-top", 31 | "indent", 32 | "mark-callable-contracts", 33 | "max-line-length", 34 | "max-states-count", 35 | "modifier-name-mixedcase", 36 | "multiple-sends", 37 | "no-complex-fallback", 38 | "no-console", 39 | "no-empty-blocks", 40 | "no-global-import", 41 | "no-inline-assembly", 42 | "no-mix-tabs-and-spaces", 43 | "no-simple-event-func-name", 44 | "no-spaces-before-semicolon", 45 | "not-rely-on-block-hash", 46 | "not-rely-on-time", 47 | "no-unused-vars", 48 | "ordering", 49 | "payable-fallback", 50 | "private-vars-leading-underscore", 51 | "quotes", 52 | "reason-string", 53 | "reentrancy", 54 | "separate-by-one-line-in-contract", 55 | "space-after-comma", 56 | "statement-indent", 57 | "state-visibility", 58 | "two-lines-top-level-separator", 59 | "use-forbidden-name", 60 | "var-name-mixedcase", 61 | "visibility-modifier-order", 62 | } 63 | 64 | def parse(exit_code, log, output): 65 | findings, infos = [], set() 66 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 67 | 68 | for line in log: 69 | if ":" in line: 70 | s_result = line.split(":") 71 | if len(s_result) != 4: 72 | continue 73 | (file, lineno, column, end_error) = s_result 74 | if "]" not in end_error: 75 | continue 76 | message = end_error[1:end_error.index("[") - 1] 77 | level = end_error[end_error.index("[") + 1: end_error.index("/")] 78 | name = end_error[end_error.index("/") + 1: len(end_error) - 1] 79 | findings.append({ 80 | "filename": file, 81 | "line": int(lineno), 82 | "column": int(column), 83 | "message": message, 84 | "level": level.lower(), 85 | "name": name 86 | }) 87 | 88 | return findings, infos, errors, fails 89 | -------------------------------------------------------------------------------- /tools/solhint-3.3.8/scripts/do_solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | BIN="$2" 5 | 6 | export PATH="$BIN:$PATH" 7 | chmod +x "$BIN/solc" 8 | 9 | solhint -f unix "$FILENAME" 10 | -------------------------------------------------------------------------------- /tools/solhint/config.yaml: -------------------------------------------------------------------------------- 1 | alias: 2 | - solhint-3.3.8 3 | -------------------------------------------------------------------------------- /tools/teether/config.yaml: -------------------------------------------------------------------------------- 1 | name: teEther 2 | image: smartbugs/teether:04adf56 3 | origin: https://github.com/nescio007/teether 4 | version: '#04adf56' 5 | info: Analysis and automatic exploitation framework for Ethereum smart contracts. 6 | runtime: 7 | command: "'$FILENAME' 0x1234 0x1000 +1000" 8 | -------------------------------------------------------------------------------- /tools/teether/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image 2 | FROM python:3.8-slim 3 | 4 | RUN apt-get update; apt-get install -y git build-essential 5 | 6 | # Clone the repository 7 | RUN git clone https://github.com/nescio007/teether.git 8 | WORKDIR /teether 9 | RUN git checkout 04adf56 10 | 11 | RUN mv teether/cfg/bb.py teether/cfg/bb.orig.py; \ 12 | sed '/Failed to compute jump target for BB\|Jump to invalid address/s/^/#/' \ 13 | teether/cfg/bb.orig.py > teether/cfg/bb.py 14 | 15 | RUN python3 setup.py install 16 | 17 | ENTRYPOINT [ "python3", "bin/gen_exploit.py" ] -------------------------------------------------------------------------------- /tools/teether/findings.yaml: -------------------------------------------------------------------------------- 1 | Ether leak: 2 | classification: SWC-105, DASP-2 3 | -------------------------------------------------------------------------------- /tools/teether/parser.py: -------------------------------------------------------------------------------- 1 | import sb.parse_utils 2 | 3 | VERSION = "2023/02/24" 4 | 5 | FINDINGS = { "Ether leak" } 6 | 7 | 8 | def parse(exit_code, log, output): 9 | findings, infos = [], set() 10 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 11 | 12 | errors.discard("EXIT_CODE_1") # there will be an exception in fails anyway 13 | for f in list(fails): # make a copy of fails, so we can modify it 14 | if f.startswith("exception (teether.evm.exceptions."): # reported as error below 15 | fails.remove(f) 16 | elif (f.startswith('exception (z3.z3types.Z3Exception: b"Argument ') or 17 | f.startswith("exception (z3.z3types.Z3Exception: b'Argument ")): 18 | fails.remove(f) 19 | fails.add("exception (z3.z3types.Z3Exception: Argument does not match function declaration)") 20 | 21 | exploit = [] 22 | analysis_completed = False 23 | for line in log: 24 | if line.startswith("INFO:root:Could not exploit any RETURN+CALL"): 25 | infos.add("Could not exploit any RETURN+CALL") 26 | analysis_completed = True 27 | elif line.startswith("WARNING:root:No state-dependent critical path found, aborting"): 28 | infos.add("No state-dependent critical path found") 29 | analysis_completed = True 30 | elif line.startswith("eth.sendTransaction"): 31 | exploit.append(line) 32 | analysis_completed = True 33 | elif line.startswith("ERROR:root:"): 34 | error = line[11:] 35 | 36 | if error.startswith("Failed path due to "): 37 | e = error[19:] 38 | 39 | # Skip errors already reported as an exception 40 | if e.startswith("b'Argument ") or any(e in f for f in fails): 41 | continue 42 | 43 | if e.startswith("Symbolic code index"): 44 | error = "Failed path due to Symbolic code index" 45 | elif e.startswith("balance of symbolic address"): 46 | error = "Failed path due to balance of symbolic address" 47 | 48 | errors.add(error) 49 | 50 | if log and not analysis_completed: 51 | infos.add("analysis incomplete") 52 | if not fails and not errors: 53 | fails.add("execution failed") 54 | 55 | if exploit: 56 | findings = [ { "name": "Ether leak", "exploit": exploit } ] 57 | 58 | return findings, infos, errors, fails 59 | 60 | -------------------------------------------------------------------------------- /tools/vandal/config.yaml: -------------------------------------------------------------------------------- 1 | name: Vandal 2 | origin: https://github.com/usyd-blockchain/vandal 3 | version: "#d2b0043" 4 | info: Vandal is a static program analysis framework for Ethereum smart contract bytecode. 5 | image: smartbugs/vandal 6 | runtime: 7 | entrypoint: "'$BIN/do_runtime.sh' '$FILENAME' '$TIMEOUT' '$BIN'" 8 | bin: scripts 9 | output: /results 10 | # command: /runVandal.sh $FILENAME 11 | -------------------------------------------------------------------------------- /tools/vandal/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image 2 | FROM ubuntu:20.04 3 | 4 | # Install Python and build tools 5 | RUN \ 6 | apt-get update && \ 7 | # apt-get install -y build-essential software-properties-common libssl-dev wget && \ 8 | apt-get install -y python3 python3-pip git curl lsb-release 9 | # python-dev python-pip git psmisc lsof 10 | 11 | # Install souffle 12 | #RUN curl -s https://packagecloud.io/install/repositories/souffle-lang/souffle/script.deb.sh | bash 13 | #RUN apt-get install souffle 14 | 15 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata 16 | 17 | RUN apt install -y \ 18 | bison \ 19 | build-essential \ 20 | clang \ 21 | cmake \ 22 | doxygen \ 23 | flex \ 24 | g++ \ 25 | git \ 26 | libffi-dev \ 27 | libncurses5-dev \ 28 | libsqlite3-dev \ 29 | make \ 30 | mcpp \ 31 | python \ 32 | sqlite \ 33 | zlib1g-dev 34 | 35 | RUN git clone https://github.com/souffle-lang/souffle /souffle 36 | 37 | RUN cd /souffle; cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local 38 | RUN cd /souffle; cmake --build build --target install 39 | 40 | # Clone the repository 41 | RUN git clone https://github.com/usyd-blockchain/vandal.git /vandal 42 | 43 | WORKDIR /vandal 44 | 45 | RUN pip3 --no-cache-dir install --upgrade setuptools pip 46 | 47 | RUN pip3 install -r requirements.txt 48 | 49 | # Script that calls vandal 50 | WORKDIR / 51 | COPY scripts/runVandal.sh runVandal.sh 52 | RUN chmod +x runVandal.sh 53 | -------------------------------------------------------------------------------- /tools/vandal/docker/scripts/runVandal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME=$1 4 | 5 | mkdir -p results; cd results; /vandal/bin/analyze.sh ${FILENAME} /vandal/datalog/demo_analyses.dl; du -s *.csv | grep -v ^0 6 | -------------------------------------------------------------------------------- /tools/vandal/findings.yaml: -------------------------------------------------------------------------------- 1 | CheckedCallStateUpdate: {} 2 | Destroyable: 3 | classification: SWC-106, DASP-2 4 | OriginUsed: 5 | classification: SWC-115, DASP-2 6 | ReentrantCall: 7 | classification: SWC-107, DASP-1 8 | UncheckedCall: 9 | classification: SWC-104, DASP-4 10 | UnsecuredValueSend: 11 | classification: SWC-105, DASP-2 12 | -------------------------------------------------------------------------------- /tools/vandal/parser.py: -------------------------------------------------------------------------------- 1 | import io, os, tarfile 2 | import sb.parse_utils 3 | 4 | VERSION = "2023/02/27" 5 | 6 | FINDINGS = ( 7 | "CheckedCallStateUpdate", 8 | "Destroyable", 9 | "OriginUsed", 10 | "ReentrantCall", 11 | "UnsecuredValueSend", 12 | "UncheckedCall" 13 | ) 14 | 15 | MAP_FINDINGS = { 16 | "checkedCallStateUpdate.csv": "CheckedCallStateUpdate", 17 | "destroyable.csv": "Destroyable", 18 | "originUsed.csv": "OriginUsed", 19 | "reentrantCall.csv": "ReentrantCall", 20 | "unsecuredValueSend.csv": "UnsecuredValueSend", 21 | "uncheckedCall.csv": "UncheckedCall" 22 | } 23 | 24 | ANALYSIS_COMPLETE = ( 25 | "+ /vandal/bin/decompile", 26 | "+ souffle -F facts-tmp", 27 | "+ rm -rf facts-tmp" 28 | ) 29 | 30 | DEPRECATED = "Warning: Deprecated type declaration" 31 | CANNOT_OPEN_FACT_FILE = "Cannot open fact file" 32 | 33 | def parse(exit_code, log, output): 34 | findings, infos = [], set() 35 | errors, fails = sb.parse_utils.errors_fails(exit_code, log) 36 | errors.discard("EXIT_CODE_1") # = no findings; EXIT_CODE_0 = findings 37 | 38 | analysis_complete = set() 39 | for line in log: 40 | if DEPRECATED in line: 41 | infos.add(DEPRECATED) 42 | continue 43 | if CANNOT_OPEN_FACT_FILE in line: 44 | fails.add(CANNOT_OPEN_FACT_FILE) 45 | continue 46 | for indicator in ANALYSIS_COMPLETE: 47 | if indicator in line: 48 | analysis_complete.add(indicator) 49 | break 50 | 51 | if log and (len(analysis_complete) < 3 or CANNOT_OPEN_FACT_FILE in fails): 52 | infos.add("analysis incomplete") 53 | if not fails and not errors: 54 | fails.add("execution failed") 55 | if CANNOT_OPEN_FACT_FILE in fails and len(fails) > 1: 56 | fails.remove(CANNOT_OPEN_FACT_FILE) 57 | 58 | if output: 59 | try: 60 | with io.BytesIO(output) as o, tarfile.open(fileobj=o) as tar: 61 | for fn in tar.getnames(): 62 | if not fn.endswith(".csv"): 63 | continue 64 | indicator = os.path.basename(fn) 65 | try: 66 | contents = tar.extractfile(fn).read() 67 | except Exception as e: 68 | fails.add(f"problem extracting {fn} from output archive: {e}") 69 | continue 70 | for line in contents.splitlines(): 71 | finding = { 72 | "name": MAP_FINDINGS[indicator], 73 | "address": int(line.strip(),16) 74 | } 75 | findings.append(finding) 76 | except Exception as e: 77 | fails.add(f"error parsing results: {e}") 78 | else: 79 | # parsing result of old Smartbugs 80 | for line in log: 81 | for indicator in MAP_FINDINGS: 82 | if indicator in line: 83 | findings.append({"name": MAP_FINDINGS[indicator]}) 84 | break 85 | 86 | return findings, infos, errors, fails 87 | 88 | -------------------------------------------------------------------------------- /tools/vandal/scripts/do_runtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | FILENAME="$1" 4 | TIMEOUT="$2" 5 | BIN="$3" 6 | 7 | mkdir -p /results 8 | cd /results 9 | /vandal/bin/analyze.sh "$FILENAME" /vandal/datalog/demo_analyses.dl 10 | du -s *.csv | grep -v ^0 11 | --------------------------------------------------------------------------------