主页 > imtoken钱包dapp图标 > Ethereum Contract Audit CheckList《以太坊智能合约设计缺陷》影响分析报告

Ethereum Contract Audit CheckList《以太坊智能合约设计缺陷》影响分析报告

imtoken钱包dapp图标 2023-03-18 06:50:46

一、简介

在智创宇404区块链安全研究团队整理输出的《知乎创宇以太坊合约审计清单》中,将“条件竞争问题”和“循环DoS问题”统称为“以太坊智能合约设计”。 缺陷问题”。

“昊天塔(HaoTian)”是智创宇404区块链安全研究团队自主研发的区块链智能合约监控、扫描、分析、审计的安全自动化平台。 针对上述《知晓创宇以太坊合约审计清单》中的“以太坊智能合约设计缺陷”,我们利用该平台对全网公开的智能合约代码进行了扫描分析。 详情见下文:

2. 漏洞详情

1、有条件的比赛

2016 年 11 月 29 日,Mikhail Vladimirov 和 Dmitry Khovratovich 发表了一篇文章《ERC20 API: An Attack Vector on Approve/TransferFrom Methods》[1],其中提到了 ERC20 标准中隐藏的一个问题,条件竞争。

这是将出现在批准功能中的典型示例。 approve一般用于授权,比如授权别人拿走自己的token。 整个过程如下:

用户A授权给用户B合约以太坊,限制为100个代币

用户A认为100个token数量太多,再次调用approve尝试将数量改为50个

用户B在待处理交易(打包前)看到了交易

用户B构造一个提取100个代币的交易,通过条件竞争,在修改额度前打包交易,成功提取100个代币

用户B发起第二笔交易并提取50个代币,用户B成功拥有150个代币

要理解上面的条件竞争原理,首先要对以太坊的打包交易逻辑有一个基本的了解。

简单的说

只有当交易被包含在区块中时,它才是不可变的

区块将优先处理 gasprice 较高的交易

以太坊合约地址_以太坊智能合约的众筹_合约以太坊

因此,当用户B在需要打包的地方看到修改后的交易时,他可以通过构造一个gasprice更高的交易来竞争,并在修改后的交易之前打包这个交易,这就产生了问题。

下面代码存在条件竞争的问题

function approve(address _spender, uint256 _value) public returns (bool success){

津贴[msg.sender][_spender] = _value;

返回真

2.循环DoS问题

在以太坊代码中,循环是一种很常见的结构,但是由于以太坊智能合约的特殊性,循环中有很多需要特别注意的地方,存在潜在的合约问题和安全隐患。

1)循环消耗问题

在以太坊中,每笔交易都会消耗一定的gas,交易的复杂度越高,交易的gasprice也越高。 在区块链上,每个区块都有一个最大的gas消耗量限制,而在矿工的最优收益计划中,如果一笔交易的gas消耗量过大,它会倾向于将这笔交易排除在区块之外。 结果,交易失败。

因此,合约中的循环次数不宜过多,循环中的代码也不宜过于复杂。

结构收款人{

地址地址;

uint256 value;}Payee payees[];uint256 nextPayeeIndex;function payOut() {

uint256 i = nextPayeeIndex;

以太坊智能合约的众筹_合约以太坊_以太坊合约地址

while (i < payees.length && msg.gas > 200000) {

收款人[i].addr.send(收款人[i].value);

我++;

}

nextPayeeIndex = i;}

如果上述代码地址列表过长,可能会导致交易失败。

2018 年 7 月 23 日,Seebug Paper 发表了《区块链代币首次自动化攻击分析》[3],在攻击合约中提到了这种 gas 优化方式。

2)循环安全问题

在以太坊中,循环次数应尽可能由用户控制,攻击者可能会使用过大的循环来完成 DoS 攻击。

函数分发(地址[]地址)onlyOwner {

for (uint i = 0; i < addresses.length; i++) {

//传输代码

}}

当攻击者不断增加地址列表的长度,强制函数执行次数过多时,合约无法正常维护,函数也无法执行。

合约以太坊_以太坊智能合约的众筹_以太坊合约地址

2016年,GovernMental合约代币遭到恶意攻击[4],导致地址列表过长无法执行,合约被困1100多ETH。

3. 漏洞范围

此类问题可以利用昊天平台的智能合约审计功能进行精准扫描。

合约以太坊_以太坊智能合约的众筹_以太坊合约地址

根据昊天平台智能合约审计功能规则,我们共扫描全网39548个合约代码,共24791个合约涉及此类问题。

1、有条件的比赛

截至2018年8月10日,我们共找到22,981个合约代码为竞争审批条件,其中15,325个合约仍处于交易状态,交易量最高的10个合约如下:

合约以太坊_以太坊智能合约的众筹_以太坊合约地址

2.循环DoS问题

截至2018年8月10日,我们共发现1810个合约代码存在潜在循环DOS问题,其中1740个合约仍处于交易状态,交易量最高的10个合约如下:

以太坊合约地址_以太坊智能合约的众筹_合约以太坊

4、修复方法

1)条件竞争

有很多关于如何解决这个问题的讨论。 由于这是底层特性的问题,很难在智能合约层面解决。 在代码级别,我们建议添加

以太坊合约地址_合约以太坊_以太坊智能合约的众筹

require((_value == 0) || (allowance[msg.sender][_spender] == 0));

加上这个条件,每次修改权限时把quota改成0,再把quota改成对应的值。

在这种情况下,合约管理者可以通过日志或者其他方式判断是否存在条件竞争,从风险控制的角度提醒合约管理者注意问题的发生。 示例代码如下:

function approve(address _spender, uint256 _value) isRunning validAddress returns (bool success) {

要求(_value == 0 || allowance[msg.sender][_spender] == 0);

津贴[msg.sender][_spender] = _value;

批准(消息。发件人,_spender,_value);

返回真;

}

2)环路DoS问题

在遇到循环DoS问题的场景中,最常见的功能就是给多个用户转账。

这里推荐的代码是尽量避免用户能够控制循环的深度。 如果无法避免,请尝试使用 withdrawFunds 之类的功能。 循环中只分配给用户的提币权限,让用户自己提币。 这个操作可以大大节省gas费用,也可以在一定程度上避免可能出现的问题。 代码如下所示:

函数分发(地址[]地址)onlyOwner {

for (uint i = 0; i < addresses.length; i++) {

以太坊智能合约的众筹_合约以太坊_以太坊合约地址

如果 (address_claimed_tokens[addresses[i]] == 0) {

余额[所有者] -= transferAmount;

余额[地址[i]] += transferAmount;

address_claimed_tokens[addresses[i]] += transferAmount;

转移(所有者,地址[i],转移金额);

}

}}

5.一些想法

在分析了很多智能合约存在的漏洞和合约后,我发现有一类特殊的问题。 这些问题的根源在于以太坊智能合约本身的设计缺陷,而开发者对此并没有清晰的认识。 认可导致合同本身存在一些隐患。

文章中提到的条件竞争是一个特殊的问题。 这里的条件竞争涉及到智能合约的底层实现逻辑。 封装逻辑本身存在条件竞争。 我们无法在代码层面避免这个问题,但是对于开发者来说,比起因为这个问题无缘无故丢失代币,更重要的是合约管理器能够监控每一笔交易的结果,所以我们添加操作设置 0 来提醒合约管理者和代币持有者问题,尽量避免此类操作的发生。

循环 Dos 问题是开发人员的难题。 每一次操作都是一次交易,每一次交易都需要消耗gas。 交易越复杂,消耗的 gas 就越多。 在区块链上,每个区块都有一个最大耗气量值是有限的,而在矿工的最优收益计划中,如果某笔交易的耗气量过大,则该笔交易往往会被排除在区块之外,导致交易失败. 这直接导致需要在交易中尽可能优化gas成本,避免交易失败。

当我们扫描监控全网的公开合约代码时,不难发现大量开发者并没有注意到这些问题,而条件竞争问题甚至影响广泛,超过半数的公开代码被影响到的。

在此我们建议所有开发者重新审视自己的合约代码,检查设计缺陷合约以太坊,避免不必要的麻烦和安全问题。

▼智能合约审计服务▼

针对目前主流的以太坊应用,智创宇提供专业、权威的智能合约审计服务,避免因合约安全问题造成的财产损失,为各类以太坊应用的安全保驾护航。

知乎创宇404智能合约安全审计团队: