主页 > imtoken钱包dapp图标 > Ethereum Contract Audit CheckList《以太坊智能合约设计缺陷》影响分析报告
Ethereum Contract Audit CheckList《以太坊智能合约设计缺陷》影响分析报告
一、简介
在智创宇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智能合约安全审计团队: