主页 > imtoken最新版官网 > Ethereum Contract Audit CheckList《以太坊智能合约编码设计问题》影响分析报告

Ethereum Contract Audit CheckList《以太坊智能合约编码设计问题》影响分析报告

imtoken最新版官网 2023-12-22 05:13:09

siteqq.com 以太坊智能合约_以太坊智能合约是什么_以太坊合约交易

作者:LoRexxar'@知道创宇404区块链安全研究组

时间:2018-09-21

系列文章:

一、简介

智创宇404区块链安全研究团队编译输出的《知乎创宇以太坊合约审计CheckList》中,“地址初始化问题”、“判断函数问题”、“余额判断问题”、“传递函数问题”、“代码外部调用设计”问题”、“错误处理”、“弱随机数问题”等问题统称为“以太坊智能合约编码设计问题”。

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

2. 漏洞详情

以太坊智能合约是以太坊概念中一个非常重要的概念。 以太坊基于solidity语言实现了以太坊虚拟机(Ethereum Virtual Machine),允许用户在链上部署智能合约代码。 通过智能合约,人们可以完成想要的合约。

我们这次提到的编码设计问题,与EVM的底层设计有很大关系。 由于EVM的特性,智能合约有很多不同于其他语言的特性。 当开发人员没有注意到这些问题时,很容易出现潜在的问题。

1.解决初始化问题

在 EVM 中,所有与地址相关的初始化都被赋予初始值 0。

如果地址变量等于 0,则表示该变量可能未初始化或发生了未知错误。

如果开发者在代码中初始化了一个地址变量,但没有赋初值,或者用户发起某个操作时,错误地没有给地址变量赋值,但是这个变量需要在以下代码,有可能导致不必要的安全隐患。

2.判断函数问题

在智能合约中,有一个非常重要的概念验证。 出现以下问题主要是合约代币的内部交易。

但是如果涉及关键判断(比如余额判断),影响交易结果,当交易出现错误时,我们需要回滚已经执行的交易结果,EVM不会检查返回结果的交易功能。 如果我们使用return false,EVM就无法得到这个错误,就会导致上一篇文章中提到的虚假充值问题。

在智能合约中,我们需要抛出这个错误,让EVM获取错误,触发底层的revert命令回滚交易。

在 solidity 中扮演这个角色的是 require 函数。 有意思的是,在solidity中,还有一个函数叫做assert,它和require的不同之处在于,它对应的是底层的一条null指令。 EVM在这里执行时,会报错退出,不会触发回滚。

转化为一个直观的交易,如果我们使用assert函数来验证,assert会消耗掉所有剩余的gas。 并且 require 会触发回滚操作。

assert 在验证中表现出强一致性。 除了检查固定变量,require 更适合这种情况。

3.平衡判断问题

在智能合约中,经常会有对用户余额的判断,尤其是在账户刚建立的时候。 很多合约在创建合约时会根据余额为0来判断合约的初始状态。 这是错误的行为。

在智能合约中,永远不可能阻止其他人强制转账给你,即使回退函数抛出。 攻击者可以创建一个带有余额的新合约,然后调用selfdestruct(victimAddress)销毁它,这样余额就会被强行转移给目标。 在此过程中,不会调用目标合约的代码,因此无法从代码层面进行防范。

值得注意的是,在打包过程中,攻击者可以通过条件竞争在合约创建前进行转账,使得合约创建时余额为0。

4.传递函数问题

在智能合约中,涉及转账的操作是最常见的。 在solidity中,提供了transfer转账/发送两个函数。

当传输/发送函数的目标是合约时,将调用合约中的回退函数。 但是当fallback函数执行错误时,传递函数会抛出错误并回滚,而send会返回false。 如果您在使用发送功能进行交易时没有及时做出判断以太坊合约交易,可能会导致转账失败但余额减少。

function withdraw(uint256 _amount) public { require(balances[msg.sender] >= _amount); 余额[msg.sender] -= _amount; etherLeft -= _amount; msg.sender.send(_amount); }

上面给出的代码中,send()函数是用来转账的,因为这里没有验证send()的返回值。 如果msg.sender为合约账户调用fallback()失败,send()返回false,最终导致账户余额减少,却没有拿到钱。

5.代码外部调用设计问题

在智能合约的设计中,有一个非常重要的概念就是外部调用。 要么调用外部合约,要么调用其他账户。 这是智能合约设计中非常普遍的想法。 最常见的是转账操作,这是一个典型的外部调用。

但是外部调用本身就是一个容易出错的操作,在与外部合约/用户交互时,谁也不能确定能不能流畅。 让我举一个常见的合约代币例子

合同拍卖{地址highestBidder; uint 最高出价; function bid() payable { if (msg.value < highestBid) throw; if (highestBidder != 0) { if (!highestBidder.send(highestBid)) { // 可能发生错误 throw; } } highestBidder = msg.sender; highestBid = msg.value; }}

上述代码在传输出错时可能会导致进一步的错误。 如果在循环中调用bid函数,更容易导致循环中途出错。 在上面提到的ddos优化问题中,这也是一个非常典型的例子。 .

而这是一个典型的推送操作,意味着合约主动与外界进行交互。 在这种情况下,容易出现问题,难以定位以太坊合约交易,难以修复,从而导致潜在问题。

6.错误处理

在智能合约中,有一些与地址底层操作相关的方法

address.call() address.callcode() address.delegatecall() address.send()

它们都有一个典型的特点,就是遇到错误不会抛出错误,而是会返回错误并继续执行。

作为 EVM 设计的一部分,如果被调用的合约不存在,以下函数将返回 True。 如果合约开发者不注意这个问题,那么可能就会出问题。

调用、委托调用、调用代码、静态调用

#4-Unchecked-Return-Values-For-Low-Level-Calls

7.弱随机数问题

智能合约是借助 EVM 在区块链上运行的合约代码。 它最大的特点就是公开、不可篡改。 而如何在合约上生成​​随机数就成了一个大问题。

Fomo3D合约在空投奖励的随机数生成中引入区块信息作为随机数种子生成的参数。 因此,随机数种子只受合约地址影响,不能完全随机。

功能空投()私人视图返回(bool){ uint256 seed = uint256(keccak256(abi.encodePacked((block.timestamp)。添加(block.difficulty)。添加((uint256(keccak256(abi.encodePacked(block.coinbase) ))) / (now)).add (block.gaslimit).add ((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)).add (block.number) ))); 如果((种子 - ((种子 / 1000)* 1000))< airDropTracker_)返回(真); 否则返回(假);}

上面的代码直接导致了Fomo3d羊毛事件的诞生。 现实世界损失巨大,超过数千eth。

最后的赢家()

3. 漏洞范围

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

以太坊智能合约是什么_以太坊合约交易_siteqq.com 以太坊智能合约

根据昊天平台智能合约扫描功能规则,我们共扫描全网42538个合约代码,其中35107个合约地址初始化有问题,4262个合约判断功能有问题,173个合约有余额判断问题。 930个合约存在传递函数问题,349个合约存在弱随机数问题,2300个合约调用了block.timestamp,超过一半的合约存在此类安全隐患。

1.解决初始化问题

截至2018年9月21日,我们共发现35107个合约代码存在地址初始化问题,存在安全隐患。

2.判断函数问题

截至2018年9月21日,我们共发现判断功能存在问题的合约代码4262个,存在安全隐患。

3.平衡判断问题

截至2018年9月21日,我们共发现173个存在余额判断问题的合约代码,其中165个仍处于交易状态,交易量最高的10个合约如下:

以太坊智能合约是什么_siteqq.com 以太坊智能合约_以太坊合约交易

4.传递函数问题

截至2018年9月21日,我们共发现930个传递函数问题的合约代码,其中873个仍处于交易状态,交易量最高的10个合约如下:

以太坊智能合约是什么_siteqq.com 以太坊智能合约_以太坊合约交易

5.弱随机数问题

截至2018年9月21日,我们共发现349个存在弱随机数问题的合约代码,其中272个仍处于交易状态,交易量最高的10个合约如下:

siteqq.com 以太坊智能合约_以太坊合约交易_以太坊智能合约是什么

截至2018年9月21日,我们查到2300个调用block.timestamp的合约代码,其中2123个合约代码还处于交易状态,交易量最高的10个合约如下:

以太坊智能合约是什么_siteqq.com 以太坊智能合约_以太坊合约交易

4、修复方法

1.解决初始化问题

对于涉及地址的函数,建议加上require(_to!=address(0))校验,有效避免用户误操作或未知错误造成不必要的损失

2.判断函数问题

对于正常的判断,优先使用require来判断结果。

对于固定变量的检查,使用assert函数可以避免一些未知的问题,因为它会强行终止合约,使其失效。 在一些固定的条件下,assert更适合

3.平衡判断问题

不要在合约的任何地方假设合约的余额,尤其不要以合约创建时为0来判断合约的初始状态。 攻击者可以使用各种方法强制传输。

4.传递函数问题

完成交易时,建议默认使用transfer函数而不是send来完成交易。

5.代码外部调用设计问题

Prefer pull over push for external contracts. 如上述transfer function,通过授予withdrawal权限,可以将主动行为转化为被动行为

合同拍卖{地址highestBidder; uint 最高出价; 映射(地址=> uint)退款; function bid() payable external { if (msg.value < highestBid) throw; if (highestBidder != 0) { 退款[highestBidder] += highestBid ; // 记录退款 } highestBidder = msg.sender; highestBid = msg.value; } function withdrawRefund() external { uint refund = refunds[msg.sender]; 退款[msg.sender] = 0; if (!msg .sender.send(refund)) { 退款[msg.sender] = 退款; // 如果转账错误,可以赎回} }}

通过构造withdraw,用户可以执行合约提取余额。

6.错误处理

当合约涉及call等对地址底层进行操作的方法时,做好合理的错误处理

if(!someAddress.send(55)) { // 一些失败代码}

包括目标合约不存在时,也需要考虑。

7.弱随机数问题

智能合约上的随机数生成方式需要更多考虑

在合约中处理此类应用时,考虑更合适的生成方式和合理的使用顺序非常重要。

这里有一个比较合理的随机数生成方式hash-commit-reveal,即玩家提交一个行动计划,然后将行动计划进行hash后提交给后台。 后端生成对应的hash值,再生成对应的随机数reveal,返回对应的随机数进行commit。 这样服务端就得不到行动计划,客户端也得不到随机数。

有一个很棒的实现代码是dice2win随机数生成代码。

当然在一些简单的场景下hash-commit也是一个很好的实现方式。 即玩家提交行动计划的哈希,然后生成一个随机数,再提交行动计划。

5.一些想法

在探索智能合约最佳实践的过程中,我逐渐发现智能合约存在很多只出现在智能合约中的问题。 这些问题大部分都是由EVM的特殊特性引起的,但是开发者并不知道这些特性导致了很多潜在的安全问题。

我把这类问题归结为编码设计问题。 开发者在编码设计阶段就可以关注这些问题,避免大部分潜在的安全问题。

▼智能合约审计服务▼

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

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