智能合约安全工具实用指南第二部分:Slither

本文介绍了Slither静态分析工具,包括其安装方法、功能特性及如何使用该工具进行Solidity智能合约的分析。作者演示了如何通过Slither检测合约中的潜在漏洞,并讨论了工具在CI环境中的适用性,强调了静态分析的优缺点。值得注意的是,文章提供了详细的代码示例和操作步骤,适合软件开发者和区块链初学者阅读。

警告

本文并不是对自动化分析器的评级。我使用它们来分析我自己的合约:故意添加伪错误并研究响应。这不是一种“好与坏”的研究类型,这类任务需要对大量合约进行盲审,考虑到这类软件的特性,实际结果不会非常精确。在某个特定合约中的一个小错误可能会使分析器逻辑中的一大部分失效,而简单的启发式特征,比如找到竞争对手简单忘记添加的典型错误,则会提升分析器的水平。此外,合约编译错误可能会影响结果。所有被审查的软件都是相当新的,并且在不断开发中,因此严重错误不应被视为不可解决。在本文中,我们不是为了帮助读者决定哪个分析器更好,而是展示不同的代码分析方法以及如何选择正确的方法。我们建议同时使用多个工具,为审核合约选择最合适的一个。本文与软件的当前版本有关,当你阅读本文时,许多内容可能会发生变化。

分析器: Slither

描述: 针对 Solidity 的开源静态分析框架

Github: https://github.com/trailofbits/slither

Slither 是一个用 Python 编写的静态代码分析框架。它可以跟踪变量、函数调用并检测以下漏洞列表:https://github.com/trailofbits/slither#detectors。每个漏洞都包含一个链接及简短描述,因此如果你是 Solidity 新手,最好熟悉所有漏洞。

Slither 可以作为 Python 模块运行,并为开发者提供可自定义的审核接口。你可以在这里查看一个简单而优雅的 Slither 功能示例:

https://github.com/trailofbits/slither/blob/master/examples/scripts/functions_writing.py

我们将在文章末尾回到分析脚本。现在让我们启动 Slither:

git clone https://github.com/trailofbits/slither.git
cd slither
docker build -t slither .

并尝试分析我们的合约。

进入 constructor-eth-booking/ 目录,并尝试通过 Docker 启动 Slither:

docker run -v $(pwd)/contracts:/slither/contracts slither:latest slither contracts/flattened.sol

它打印出“源文件需要不同的编译器版本”错误(因为我们的合约需要 solc=0.4.20),我们必须在 Slither Docker 中设置版本“solc=0.4.20”。为此,让我们根据指南的第 2 项 Part 1 修改 Slither Dockerfile,即在 Dockerfile 底部的某处添加以下行(https://github.com/trailofbits/slither/blob/master/Dockerfile):

COPY --from=ethereum/solc:0.4.20 /usr/bin/solc /usr/bin

重新编译镜像,运行它,成功了!

我们看到多个关于 “pragma” 和无效变量名的警告,比如

Parameter ‘_price’ of Booking.Booking (flattened.sol#73) is not in mixedCase

分析器会产生大量关于代码风格的警告,但我们正在寻找严重的错误,并忽略次要的缺陷。抛开风格,让我们过滤掉所有的 “mixedCase” 消息:

docker run -v $(pwd)/contracts:/slither/contracts slither:latest slither contracts/flattened.sol 2>&1 | fgrep -v 'mixedCase'

让我们跳过绿色的行,专注于所有红色的内容 :) 这里是 Slither 检测到的除了错误警告之外的有趣内容:

Booking.refundWithoutCancellationFee (flattened.sol#243–250) sends eth to arbitrary user
危险调用:
- client.transfer(address(this).balance) (flattened.sol#249)
参考:https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations
INFO: 检测器:
Booking.refundWithCancellationFee (flattened.sol#252–259) sends eth to arbitrary user
危险调用:
- owner.transfer(m_cancellationFee) (flattened.sol#257)
- client.transfer(address(this).balance) (flattened.sol#258)
参考:https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations

现在让我们查看合约函数的问题:

function refundWithoutCancellationFee() private {
    address client = m_client;
    m_client = address(0);
    changeState(State.OFFER);
    client.transfer(address(this).balance);
}function refundWithCancellationFee() private {
    address client = m_client;
    m_client = address(0);
    changeState(State.CANCELED);
    owner.transfer(m_cancellationFee);
    client.transfer(address(this).balance);
}

函数 refundWithoutCancellationFee() 以以下方式调用:

function rejectPayment() external onlyOwner onlyState(State.PAID) {
    refundWithoutCancellationFee();
}function refund() external onlyClient onlyState(State.PAID) {
    refundWithoutCancellationFee();
}

实际上,没有错误:调用受到 onlyOwner 和其他修饰符的保护,但 Slither 表示在 refundWithoutCancellationFee() 中没有检查地发送了以太币。这是对的还是错的?

函数 refundWithoutCancellationFee() 本身没有限制,没有修饰符,也没有内部分支。虽然它是私有的,并且通过具有适当限制的 “rejectPayment()” 和 “refund()” 包装器调用,但仍然存在忘记限制并将 refundWithoutCancellationFee() 调用暴露给攻击者的风险。因此,虽然在技术上没有漏洞,但此消息是有用的。至少,在合约代码假定将进一步开发的情况下,它是一个“警告”风险级别。在这种情况下,来自不同方的两个函数使用相同的代码,这种决定是为了节省 gas:对于一次性合约,部署费用是一个重要标准。

我对 Slither 是否对任何以太币发送表示不满进行了二次检查,并将函数主体直接移动到上面的 “rejectPayment()” 和 “refund()” 中。没有警告——现在 Slither 可以确认未经过地址验证发送以太币。开个好头!

现在让我们注释掉两个变量的初始化,以查看 Slither 将如何处理它们:

- m_fileHash = _fileHash;
+ // m_fileHash = _fileHash;
- m_price = _price;
+ // m_price = _price;

第一个初始化在漏洞风险方面并不是很重要,因为 m_fileHash 只是在合约创建后存储在区块链中。相反,m_price 是相当常见的,Slither 并不满意它被使用但未初始化:

Booking.m_price (flattened.sol#128) is never initialized. It is used in:- fallback (flattened.sol#144–156)

这是一个简单的技巧,一切如预期般顺利。

现在我们将合约中添加一个重入攻击漏洞,并在外部调用之后更改其状态。修改如下:

function refundWithoutCancellationFee() private {
address client = m_client;
-     m_client = address(0);
-     changeState(State.OFFER);
-     client.transfer(address(this).balance);
+     client.call.value(address(this).balance)();
+     m_client = address(0);
+     changeState(State.OFFER);
}

我被迫将 'transfer' 命令替换为 'call'。Slither 似乎将通过 'transfer()' 进行的重入认为是有效的,因为它以最低(2300)gas 发出调用,使得从被调用合约进行调用成为不可能。但是,如果迁移到康斯坦丁堡(Constantinople)以太坊分叉,gas 的价格会发生变化,再次引入重入攻击的风险,攻击者可以利用 ‘transfer’( https://github.com/ChainSecurity/constantinople-reentrancy)。请小心。

重入搜索结果如下:

重入在 Booking.refundWithoutCancellationFee (flattened.sol#243–253):
外部调用:
- client.call.value(address(this).balance)() (flattened.sol#245)
在调用后写入的状态变量:
- m_client (flattened.sol#246)

这太棒了!至少,Slither 不会让我们在外部调用后更改状态变量。

如果我们继续 Slither 列表中的其他漏洞,我们将要么搜索代码库中的特定方法,要么找出那些表现良好且可靠的典型模式,如果获得对 Python 代码标记的访问,即 Slither 不会错过那些著名模式。

下一步——我将添加一个特制模式,在现实生活中是愚蠢的,但完美展示静态源分析器的操作:

- client.transfer(address(this).balance
+ for (uint i=0; i < 1; i++) {
+     client.transfer(address(this).balance - 999999999999999999);
+ }

这是结果:

Booking.refundWithoutCancellationFee has external calls inside a loop:- client.transfer(address(this).balance — 999999999999999999) (flattened.sol#252)参考:https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description/_edit#calls-inside-a-loop

由于循环是单个退化的,该警告是误报,而关于“危险”算术缺失的警告则是漏报。静态分析器不用于进行类型分析、评估操作结果或调用。因此,我们应该清楚地区分静态分析特有的错误和使用动态分析识别的错误。

我承诺将讨论编写单独的测试脚本以及使用“— print”键输出有趣的合约细节。从这个角度来看,Slither 是一个优秀的 CI 工具。大型合约系统的开发者知道对安全至关重要的变量的名称:余额、佣金大小、标志,并可以编写一个测试脚本,自动阻止涉及重要变量的任何代码修改以及外部调用后状态变量的更改。由于结果高度可预测,Slither 是在挂钩中使用的优秀工具。Slither 帮助消除愚蠢的错误,识别著名的危险模式并警告开发者。它也是希望从一开始编写正确代码的 Solidity 新手的好工具。

结果

Slither 是一个多功能且灵活的工具,拥有强大、简单且易于遵循的分析脚本,使用 Python 编写,并具备卓越的 CI 兼容性。

Slither 发现了一个与以太币发送功能相关的严重警告,并检测到了所有添加的伪错误。它仅在动态分析方面失败——这是设计上不应执行的任务。否则,它将失去其主要优点——可预测性、可用性和简单性。

下次我们将讨论 Mythril 分析器。其他即将发布或正在制作的文章如下:

Part 1. 引言。编译、压平、Solidity 版本

Part 2. Slither(本文)

Part 3. Mythril

如果你有关于使用 Slither 进行项目的具体问题,请随时通过我们的 网站Telegram 联系我们。

  • 原文链接: medium.com/mixbytes/a-pr...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
sprilutskiy
sprilutskiy
江湖只有他的大名,没有他的介绍。