ItyFuzz 是一款快速的混合模糊测试工具,用于 EVM、MoveVM(WIP)等。
英文版 README / 研究论文 / 开发信息
安装 Docker 并运行适用于你的系统架构的 docker 镜像:
docker pull fuzzland/ityfuzz:stable
docker run -p 8000:8000 fuzzland/ityfuzz:stable
然后,您可以在 http://localhost:8000 访问 UI。
注意:容器使用公共 ETH RPC,可能超时或运行缓慢
发现漏洞/生成攻击所花费的时间:
| 项目名称 | 漏洞 | Mythril | SMARTIAN | Slither | ItyFuzz |
|---|---|---|---|---|---|
| AES | 业务逻辑 | Inf | 不支持 | No | 4 小时 |
| Carrot | 任意外部调用 | 17s | 11s | Yes | 1s |
| Olympus | 访问控制 | 36s | Inf | Yes | 1s |
| MUMUG | 价格操纵 | Inf | 不支持 | No | 18 小时 |
| Omni | 重入 | Inf | 不支持 | Yes* | 22 小时 |
| Verilog CTF-2 | 重入 | Inf | 不支持 | Yes* | 3s |
* Slither 仅发现重入位置,而不是如何利用重入来触发最终的错误代码。输出还包含大量的误报。
测试覆盖率:
| 数据集 | SMARTIAN | Echidna | ItyFuzz |
|---|---|---|---|
| B1 | 97.1% | 47.1% | 99.2% |
| B2 | 86.2% | 82.9% | 95.4% |
| Tests | 不支持 | 52.9% | 100% |
* B1 和 B2 包含 72 个合约。Tests 是 tests 目录中的项目。覆盖率计算为 (覆盖的指令)/(总指令 - 无效代码)。
您需要安装 libssl-dev(OpenSSL)和 libz3-dev(参见Z3 安装章节中的说明)。
# 下载依赖
git submodule update --recursive --init
cd cli/
cargo build --release你需要solc来编译智能合约。你可以使用solc-select工具来管理solc的版本。
编译智能合约:
cd ./tests/multi-contract/
solc *.sol -o . --bin --abi --overwrite --base-path ../../运行 Fuzzer:
cd ./cli/
./cli -t '../tests/multi-contract/*'Verilog CTF Challenge 2
tests/verilog-2/
合约有闪电贷款攻击+重入漏洞。攻击目标是到达Bounty.sol中的第 34 行。
具体漏洞利用过程:
0. 借k MATIC,使得 k > balance() / 10
1. 用k MATIC 调用 depositMATIC()
2. redeem(k * 1e18) --重入合约--> getBounty()
3. 返还k MATIC
使用 ItyFuzz 检测漏洞并生成具体漏洞利用过程(需要 0-200 秒):
# 在tests/verilog-2/中构建合约
solc *.sol -o . --bin --abi --overwrite --base-path ../../
# 运行fuzzer
./cli -f -t "./tests/verilog-2/*"-f 标志启用自动闪电贷款,它会 hook 所有 ERC20 外部调用,使任何用户都具有无限余额。
您可以通过提供项目目录的路径(glob)来 Fuzz 一个项目。
./cli -t '[DIR_PATH]/*'ItyFuzz 将尝试将目录中的所有工件部署到没有其他智能合约的区块链中。
项目目录中应当包含[X].abi和[X].bin文件。例如,要 fuzz 一个名为main.sol的合约,您应该
确保项目目录中存在main.abi和main.bin。
ItyFuzz 将自动检测目录中的合约之间的关联(参见tests/multi-contract),
并 fuzz 它们。
如果 ItyFuzz 无法推断合约之间的关联,您
也可以添加一个[X].address,其中[X]是合约名称,以指定合约的地址。
注意事项:
- ItyFuzz 在无任何合约的区块链上进行 fuzz, 因此您应该确保在 fuzz 之前将所有相关合约(例如,ERC20 令牌,Uniswap 等)都将部署到 ItyFuzz 的区块链中。
(可选)启用 flashloan_v2 重新构建以获得更好的结果。
sed -i 's/\"default = [\"/\"default = [flashloan_v2,\"/g' ./Cargo.toml
cd ./cli/
cargo build --release您可以通过提供地址,块和链来 fuzz 一个项目。
./cli -o -t [TARGET_ADDR] --onchain-block-number [BLOCK] -c [CHAIN_TYPE]示例:
在以太坊主网最新区块上 fuzz WETH 合约(0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2)。
./cli -o -t 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 --onchain-block-number 0 -c ETHItyFuzz 将从 Etherscan 拉取合约的 ABI 并 fuzz 它。如果 ItyFuzz 遇到 Storage 中未知的槽,它将从 RPC 同步槽。 如果 ItyFuzz 遇到对外部未知合约的调用,它将拉取该合约的字节码和 ABI。 如果它的 ABI 不可用,ItyFuzz 将使用 heimdall 对字节码进行反编译分析 ABI。
当遇到 SLOAD 与目标未初始化的时,ItyFuzz 尝试从区块链节点获取存储。有三种获取方式:
- OneByOne:一次获取一个 slot 。这是默认模式。它很慢,但不会失败。
- All:使用我们节点上的自定义 API
eth_getStorageAll一次性获取所有 slot 。这是最快的模式,但如果合约太大,可能会失败。 - Dump:使用 debug API
debug_storageRangeAt来转储存储。这只适用于 ETH(目前),并且很容易失败。
ItyFuzz 提供两种方法来传入构造函数参数。这些参数对于在部署时初始化合约的状态是必要的。
方法 1:CLI 参数
第一种方法是直接作为 CLI 参数传入构造函数参数。
当你使用 CLI
运行 ItyFuzz 时,你可以包含--constructor-args标志,后跟一个指定每个构造函数的参数的字符串。
格式如下:
cli -t 'tests/multi-contract/*' --constructor-args "ContractName:arg1,arg2,...;AnotherContract:arg1,arg2,..;"
例如,如果你有两个合约,main 和 main2,它们都有一个 bytes32 和一个 uint256 作为构造函数参数,你可以这样传入它们:
cli -t 'tests/multi-contract/*' --constructor-args "main:1,0x6100000000000000000000000000000000000000000000000000000000000000;main2:2,0x6200000000000000000000000000000000000000000000000000000000000000;"方法 2:服务器转发
第二种方法是使用我们的服务器将请求转发到用户指定的 RPC,cli 将从发送到 RPC 的交易中获取构造函数参数。
首先,进入/server目录,并安装必要的包:
cd /server
npm install然后,使用以下命令启动服务器:
node app.js默认情况下,服务器将请求转发到http://localhost:8545,这是Ganache的默认地址,如果你没有运行本地区块链,你可以使用 Ganache 启动一个。
如果你希望将请求转发到其他位置,你可以像这样指定地址作为命令行参数:
node app.js http://localhost:8546一旦服务器运行起来,你就可以使用你选择的工具将你的合约部署到 localhost:5001。
例如,你可以使用 Foundry 通过服务器部署你的合约:
forge create src/flashloan.sol:main2 --rpc-url http://127.0.0.1:5001 --private-key 0x0000000000000000000000000000000000000000000000000000000000000000 --constructor-args "1" "0x6100000000000000000000000000000000000000000000000000000000000000"最后,你可以使用--fetch-tx-data标志获取构造函数参数:
cli -t 'tests/multi-contract/*' --fetch-tx-dataItyFuzz 将从通过服务器转发到 RPC 的交易中获取构造函数参数。
macOS
git clone https://github.com/Z3Prover/z3 && cd z3
python scripts/mk_make.py --prefix=/usr/local
cd build && make -j64 && sudo make install如果构建命令仍然因找不到z3.h而失败,执行export Z3_SYS_Z3_HEADER=/usr/local/include/z3.h
Ubuntu
apt install libz3-devItyFuzz 收集遥测数据以帮助我们改进模糊器。这些数据对我们非常有价值,我们非常感谢你让我们收集它们!
ItyFuzz 收集以下类型的数据:
- ItyFuzz 的版本
- 运行模糊器的操作系统和版本
- 模糊器运行的时间
- 使用的命令行参数(不包括你的输入目录)
- 生成的目标列表和数量
- 发现的漏洞类型和数量
- 测试生成器和模糊器的统计数据
收集的数据不包括任何可以用来识别你的信息,例如 IP 地址,目录名或文件名。
默认情况下,ItyFuzz 将在每次运行结束时将遥测数据发送到我们的服务器。你可以在每次运行结束时看到一个摘要,显示发送了哪些数据。
如果你不希望 ItyFuzz 收集遥测数据,你可以在运行模糊器时使用--no-telemetry标志。
./cli -t '[DIR_PATH]/*' --no-telemetry@misc{ityfuzz,
title={ItyFuzz: Snapshot-Based Fuzzer for Smart Contract},
author={Chaofan Shou and Shangyin Tan and Koushik Sen},
year={2023},
eprint={2306.17135},
archivePrefix={arXiv},
primaryClass={cs.CR}
}
