介绍:
在本文中,我将解释如何使用一些智能合约安全工具(Mythril、MythX、Slither、Manticore、Security、SmartCheck)来查找和利用以太坊智能合约中的安全漏洞。文章将涵盖基本原理和高级技术,例如测试安全属性、比较这些安全工具。我们将以Capture of the Ether挑战中名为Fifty years的智能合约为例子用上面的工具测试。Letsgo!
漏洞合约代码
下面我们将这个漏洞合约作为例子测试:
pragma solidity ^0.4.21;
contract FiftyYearsChallenge {
struct Contribution {
uint256 amount;
uint256 unlockTimestamp;
}
Contribution[] queue;
uint256 head;
address owner;
function FiftyYearsChallenge(address player) public payable {
require(msg.value == 1 ether);
owner = player;
queue.push(Contribution(msg.value, now + 50 years));
}
function isComplete() public view returns (bool) {
return address(this).balance == 0;
}
function upsert(uint256 index, uint256 timestamp) public payable {
require(msg.sender == owner);
if (index >= head && index < queue.length) {
// Update existing contribution amount without updating timestamp.
Contribution storage contribution = queue[index];
contribution.amount += msg.value;
} else {
// Append a new contribution. Require that each contribution unlock
// at least 1 day after the previous one.
require(timestamp >= queue[queue.length - 1].unlockTimestamp + 1 days);
contribution.amount = msg.value;
contribution.unlockTimestamp = timestamp;
queue.push(contribution);
}
}
function withdraw(uint256 index) public {
require(msg.sender == owner);
require(now >= queue[index].unlockTimestamp);
// Withdraw this and any earlier contributions.
uint256 total = 0;
for (uint256 i = head; i <= index; i++) {
total += queue[i].amount;
// Reclaim storage.
delete queue[i];
}
// Move the head of the queue forward so we don't have to loop over
// already-withdrawn contributions.
head = index + 1;
msg.sender.transfer(total);
}
}
开始
MythX:
MythX 是以太坊智能合约的首要安全分析服务工具,MythX 的使命是确保开发团队避免代价高昂的错误,并使以太坊成为更安全、更值得信赖的平台。
这是使用 MythX for Remix IDE 的结果演示:
- HIGHSWC-101 | 整数上溢和下溢
加法运算的操作数没有得到充分的约束。因此,添加可能会导致整数溢出。通过检查输入来防止溢出或确保溢出被断言捕获。 - HIGHSWC-105 | 不受保护的 ETH 取款
合约创建者以外的任意发送者可以从合约账户中取款 ETH。这很可能是一个漏洞。 - MEDIUMSWC-134 | 带有硬编码气体量
的消息调用突出显示的函数调用转发固定量的气体。这是不鼓励的,因为 EVM 指令的 gas 成本在未来可能会发生变化,这可能会破坏该合约的假设。如果这样做是为了防止重入攻击,请考虑替代方法,例如检查-效果-交互模式或重入锁。
Mythril:
Mythril 是 EVM 字节码的安全分析工具。它检测为 Ethereum、Hedera、Quorum、Vechain、Roostock、Tron 和其他 EVM 兼容区块链构建的智能合约中的安全漏洞。
它使用符号执行、SMT 求解和污点分析来检测各种安全漏洞。它还用于MythX安全分析平台结合使用。
安装:
pip3 install mythril
扫描合约
myth analyze contract.sol
结果
重点是三个漏洞:
- 整数溢出
- 对可预测环境变量的依赖性
- 未受保护的 ETH 取款
详细细节可以看下面:
==== Integer Overflow ====
SWC ID: 101
Severity: High
Contract: FiftyYearsChallenge
Function name: constructor
PC address: 179
Estimated Gas Usage: 23146 - 109214
The binary addition can overflow.
The operands of the addition operation are not sufficiently constrained. The addition could therefore result in an integer overflow. Prevent the overflow by checking inputs or ensure sure that the overflow is caught by an assertion.
--------------------
In file: fifrthyyear.sol:16
now + 50 years
--------------------
Initial State:
Account: [CREATOR], balance: 0x21c10c0542040001, nonce:0, storage:{}
Account: [ATTACKER], balance: 0x0, nonce:0, storage:{}
Account: [SOMEGUY], balance: 0x0, nonce:0, storage:{}
Transaction Sequence:
Caller: [CREATOR], calldata: , value: 0xde0b6b3a7640000
==== Dependence on predictable environment variable ====
SWC ID: 116
Severity: Low
Contract: FiftyYearsChallenge
Function name: withdraw(uint256)
PC address: 342
Estimated Gas Usage: 22161 - 117057
A control flow decision is made based on a predictable variable.
The block.timestamp environment variable is used in to determine a control flow decision. Note that the values of variables like coinbase, gaslimit, block number and timestamp are predictable and can be manipulated by a malicious miner. Also keep in mind that attackers know hashes of earlier blocks. Don't use any of those environment variables for random number generation or to make critical control flow decisions.
--------------------
In file: fifrthyyear.sol:43
require(now >= queue[index].unlockTimestamp)
--------------------
Initial State:
Account: [CREATOR], balance: 0x1000000000000001, nonce:0, storage:{}
Account: [ATTACKER], balance: 0x0, nonce:0, storage:{}
Account: [SOMEGUY], balance: 0x0, nonce:0, storage:{}
Transaction Sequence:
Caller: [CREATOR], calldata: fefefefefefefefefefefefeaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe, value: 0xde0b6b3a7640000
Caller: [CREATOR], function: withdraw(uint256), txdata: 0x2e1a7d4d, value: 0x0
==== Unprotected Ether Withdrawal ====
SWC ID: 105
Severity: High
Contract: FiftyYearsChallenge
Function name: withdraw(uint256)
PC address: 522
Estimated Gas Usage: 22161 - 117057
Anyone can withdraw ETH from the contract account.
Arbitrary senders other than the contract creator can withdraw ETH from the contract account. This is likely to be a vulnerability.
--------------------
In file: fifrthyyear.sol:58
msg.sender.transfer(total)
--------------------
Initial State:
Account: [CREATOR], balance: 0x2181000142640001, nonce:0, storage:{}
Account: [ATTACKER], balance: 0x42101040440090000, nonce:0, storage:
Account: [SOMEGUY], balance: 0x0, nonce:0, storage:{}
Transaction Sequence:
Caller: [CREATOR], calldata: efefefefefefefefefefefefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef, value: 0xde0b6b3a7640000
Caller: [ATTACKER], function: withdraw(uint256), txdata: 0x2e1a7d4d, value: 0x0
Slither
Slither 是一个用 Python 3 编写的 Solidity 静态分析框架。它运行一套漏洞检测器,打印有关合约细节的视觉信息,并提供一个 API 来轻松编写自定义分析。Slither 使开发人员能够发现漏洞,增强他们的代码理解力,并快速构建自定义分析原型。
安装:
使用pip
pip3 install slither-analyzer
使用git
git clone https://github.com/crytic/slither.git && cd slither
python3 setup.py install
Scan 命令
slither contract.sol
下面是扫描结果:
INFO:Detectors:
FiftyYearsChallenge.isComplete() (fifrthyyear.sol#19-21) uses a dangerous strict equality:
- address(this).balance == 0 (fifrthyyear.sol#20)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities
INFO:Detectors:
Pragma version^0.4.21 (fifrthyyear.sol#1) allows old versions
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity
INFO:Detectors:
isComplete() should be declared external:
- FiftyYearsChallenge.isComplete() (fifrthyyear.sol#19-21)
upsert(uint256,uint256) should be declared external:
- FiftyYearsChallenge.upsert(uint256,uint256) (fifrthyyear.sol#23-39)
withdraw(uint256) should be declared external:
- FiftyYearsChallenge.withdraw(uint256) (fifrthyyear.sol#41-59)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-as-external
Securify:
Securify 2.0 是以太坊基金会和ChainSecurity支持的以太坊智能合约安全扫描器。Securify 背后的核心研究是在苏黎世联邦理工学院的安全、可靠和智能系统实验室进行的。
有两种使用 Securify 的方法。在你的计算机安装它或使用网络应用程序,如果你想在“这里”安装它,你可以查看本指南。如果您想使用网络应用程序,
这里是链接:https ://securify.chainsecurity.com/
SmartCheck:
SmartCheck 是一种可扩展的静态分析工具,用于发现以 Solidity 编程语言编写的以太坊智能合约中的漏洞和其他代码问题。作为 Securify,您可以将 SmartCheck 作为 Web 应用程序使用或将其安装在您的电脑中。
确保你的机器上安装了 nodejs 和 npm
npm install @smartdec/smartcheck -g
扫描
smartcheck -p contract.sol
这是在网站上扫描的示例:
Manticore:
Manticore 是一个用于分析智能合约和二进制文件的符号执行工具。
安装系统依赖
sudo apt-get update && sudo apt-get install python3 python3-dev python3-pip -y
安装 Manticore 及其依赖
sudo pip3 install manticore[native]
结果分析:
我认为比较这些工具的最佳方法是扫描多个不同的合同并分析每次扫描的结果。这是我使用工具(MythX、Mythril、Securify、SmartCheck、Slither)选择 3 个随机智能合约进行的扫描。结果如下表所示:
结论:
结果显示,Mythx 总共发现了 18 个不同的错误,这明显高于其他对中等错误具有高识别度的工具。另一方面,Securify 在关键错误检测方面得分最高。
最后,每个工具都有特定的方法来检查智能合约的漏洞(静态分析、符号分析、正式合约验证器……),我们的实验只关注 3 个智能合约,为了得到更合理的比较,最好是分析更多合同(例如 1000S-C)