- 这两天在阅读关于自动化合约安全审计工具oyente,文章中提到了一些智能合约的安全漏洞,比较有意思,mark下来。
合约中的安全漏洞
-
1、交易顺序依赖性
- 考虑一个场景,区块链处于状态,新的区块包括两个调用同一合约的交易,例如与,在这样的场景中,用户在执行各自的调用时不确定合同处于哪个状态。例如,当交易执行时,区块链可能处于状态或者,其中---->,这取决于交易与执行的先后顺序。因此,在用户可能打算调用的合同状态和相应执行时的实际状态之间存在差异。只有挖掘块的矿工才能决定这些事务的顺序,从而决定更新的顺序。因此,合同的最终状态取决于矿工如何命令调用它的事务。我们称这种契约为交易顺序依赖(或)契约。
- 1.1 攻击
-
读者可能并不清楚,为什么对智能合约来说,依赖交易顺序是有问题的。原因有两个。首先,如果有并发调用,即使对合约的良性调用也可能会给用户带来意外的结果。其次,恶意用户可以利用合约获取更多利润,甚至窃取用户的资金。我们使用图3中的合约来解释下面的两个场景。
- 良性场景:例如如上合约同时有两笔交易进行调用,一笔来自于owner来对其reward进行更新,而另一笔交易来自于用户提交solution并获取reward。显然,用户最终获得的reward数目与owner发起的交易是否执行有关。在这些合同中,卖家经常更新价格,用户发送订单购买一些商品(图4)。根据交易顺序,用户的购买请求可以通过,也可以不通过。更糟糕的是,买家在发出购买请求时可能要支付比观察到的价格高得多的价格。
-
恶意场景:上述场景中,owner的行为是无意的。但是,如果合约的onwer的所有者想要利用合约的时序差异来给自己牟利是完全可行的,它可以支付非常少的reward就可以获得自己想要的solution。
-
2 时间戳依赖性
-
合同可能存在的下一个安全问题是使用区块时间戳作为触发条件来执行一些关键操作,例如发送资金。我们称这种契约为依赖时间戳的合约。
- 例如该合约theRun,通过随机数来选取谁是活动头奖的winner,技术上,theRun使用了先前某些区块作为随机数种子来选取winner(9~10行),但先前区块的选择是基于了当前区块的时间戳。
- 通常,时间戳设置为矿工本地系统的当前时间。同时,矿工可以改变这个值大概900秒,同时仍然有其他矿工接受这个区块。具体地说,在接收到新的块并且在检查其他有效性检查之后,矿工检查块时间戳是否大于上一个块的时间戳并且是否在其本地系统上的时间戳的900秒内。因此,敌手可以选择不同的块时间戳来操纵时间戳相关合约的结果。
- 2.1 攻击
- 矿工可以把区块时间戳设计成一个特定的值,使其获利。例如,在 theRun契约中,已知前一个块和块号的散列,也知道有助于生成随机种子的其他契约变量,如最后一次支付。因此,矿工可以预先计算并选择时间戳,以便函数random产生有利于他的结果。因此,对手可能会完全将随机种子的结果偏向于任何值,从而将头奖授予任何他喜欢的玩家。因此,theRun容易受到任何能够操纵块时间戳的敌手的攻击。
3 异常处理异常
- 在以太坊中,一个合约可以通过不同方式去调用另外一个合约。如果被调用合约出现异常,则中止,还原状态并返回false。但是,由于调用方式的不同,被调用合约的异常状态可能传播到也可能传播不到调用合约。
-
攻击
- KoET这个程序就是谁当国王谁傻逼。当前国王通过任命下一任国王时,下一任国王的报价与自己报价的差来获利。在任命新国王之前,KoET合约并不会在15行检查补偿交易的结果。因此,如果某些原因,补偿交易未能正确完成。现在国王直接gg。
- 以上情况是由于被调用方出错而导致了发送失败。接下来的情况是由于调用方的恶意用户故意导致send失败。
- 故意超过堆栈调用深度限制:以太坊虚拟机将调用堆栈的深度设置为1024。如果合约通过send或者call指令调用另外一个合约,则调用堆栈的深度会加一。因此,攻击者可以预先准备一份自己调用自身1023次的合约,导致调用KoET合约时深度到达1024,从而使得第15行的send指令失败。
- 利用堆栈调用限制来牟利:在以上攻击中,攻击者除了让其他用户损失了利益以外,并没有使得自身获利。而后有许多攻击,攻击者可以使得自己牟利。具体我们在后面章节讨论。
3.4 可重入漏洞
-
在以太坊中,当一个合约调用另外一个合约时,当前执行将等待调用完成。当调用的接收者使用调用方所处的中间状态时,这可能会导致问题。如果不考虑被叫方可能存在的恶意行为,则在编写合同时,这可能不太明显。
- 攻击样本:在上述合约中,第11行会想取款方发送其余额,这时取款方可以利用自身的fallback函数来实现对于SendBalance合约的重入,而由于第13行代码并未执行,因此会不断的从SendBalance合约的余额中取款。
网友评论