拒绝服务(DOS)攻击

未设定gas费率的外部调用

在以太坊中,每一笔交易都会携带一定数量的Gas,这是为了确保任何执行的操作都不会消耗过多的计算资源,从而避免网络拥堵或资源耗尽。当一笔交易开始执行时,它会从交易者提供的Gas总量中扣除费用,直到合约执行完成或Gas耗尽。如果在执行过程中Gas耗尽,那么交易将被回滚,且已经消耗的Gas不会退还给用户。

漏洞分析:

1, 攻击者可以通过构造高复杂度的交易或智能合约来故意消耗大量的Gas,从而正常交易无法被包含在区块中。例如,攻击者可以创建一个合约,该合约在接收到消息时执行大量计算或存储操作,消耗接近最大Gas限额的Gas量。当许多这样的交易被同时发送到网络时,它们会占据大部分甚至全部的Gas容量,导致其他用户的正常交易无法被确认,从而达到拒绝服务的效果。
2,使智能合约进入无限循环,这将导致Gas立即耗尽,交易失败并回滚。这种攻击通常发生在合约逻辑中存在错误的情况下,例如没有正确处理循环退出条件,或在递归调用中缺少终止条件。当合约进入无限循环时,它会尝试消耗所有可用的Gas,最终导致交易失败,并可能使合约处于不可用状态。

解决方法:

使用call函数时可以调试出执行操作需要的大致gas费率,在call函数指定稍大一些费率,避免攻击发生。

依赖外部的调用进展

漏洞分析:

这种情况是由于合约没有正确处理外部调用。例如,如果合约依赖于外部函数执行的结果,但合约没有对外部函数执行失败进行处理,此时如果外部调用失败或者由于外部原因而被拒绝时,会导致每次执行交易时都会因为这个失败问题导致交易回滚,合约无法继续执行。

举例:

在竞拍合约中,出价者高成为king,并把上一位king的钱transfer回去,但是如果上一位king并不能接受钱的话,那么他就会一直成为king,就相当于拒绝其他人的出高价竞拍

解决方法

在竞拍合约中尽量让合约参与者自提参与竞拍的token,其次如果确实需要对外部函数调用的结果进行处理才能进入新的状态,请考虑外部调用可能一直失败的情况,也可以添加基于时间的操作,防止外部函数调用一直无法满足require判断。

Owner的错误操作

漏洞分析:

这种拒绝服务攻击就是建立在后期运营情况下,在智能合约中通常会存在以 Owner 账户作为管理员角色,该角色通常会持有很高的权限,例如开启或暂停转账功能,当 Owner 角色操作失误或私钥丢失可能会受到非主观意义上的拒绝服务攻击。

解决方法:

建议设计多个owner地址,避免密钥遗失等问题发生时,导致合约被锁,同时一个综合系统中只有一个绝对权限的管理员是极其不安全的。

数组或映射过长

漏洞分析:

当合约中存在对传入的映射或数组循环遍历的逻辑且没有限制传入的映射或数组的长度时攻击者可以通过传入超长的映射或者数组进行循环遍历而大量消耗 Gas 从而该笔交易的 Gas 溢出,最后使得智能合约暂时或永久不可操作。

举例

在利益分发合约中,类似于公司给股东分红,如果员工人数过于多,即数组过大会导致操作执行的gas远远超于上限,从而导致交易失败,也就无法分红

解决方法:

避免需要循环操作的数组或映射能够被外部调用,同时在合理的增长过程,可以采用分区块处理的方式,避免数组或映射过大失败。

依赖库的问题

漏洞分析:

依赖外部的合约库。如果外部合约的库被删除,那么所有依赖库的合约服务都无法使用。有些合约用于接受ether,并转账给其他地址。但是,这些合约本身并没有自己实现一个转账函数,而是通过delegatecall去调用一些其他合约中的转账函数去实现转账的功能。万一这些提供转账功能的合约执行suicide或self-destruct操作的话,那么,通过delegatecall调用转账功能的合约就有可能发生ether被冻结的情况

举例

Parity 钱包遭受的第二次攻击是一个很好的例子。Parity 钱包提供了多签钱包的库合约。当库合约的函数被 delegatecall 调用时,它是运行在调用方(即:用户多签合约)的上下文里,像 m_numOwners 这样的变量都来自于用户多签合约的上下文。另外,为了能被用户合约调用,这些库合约的初始化函数都是public的。攻击者就调用初始化函数把自己设置为库函数的owner,在调用kill()函数,把库合约删除,所有的ether就被冻结了

解决方法:

继承库合约后,对于可以改变指智能合约存储状态的函数,尽量采取重写的方式,避免被恶意调用。特别是owner修饰词,转账函数。