10.slither检测器分析之二——自杀函数检测及shift汇编函数检测

  • 小驹
  • 更新于 2023-07-03 15:15
  • 阅读 919

1.理解自杀函数检测自杀函数的风险与应用场景。自杀函数可以做为一种隐藏的transfer的手段。 2.shift汇编函数与其它语言的参数不一致,容易混淆。

自杀函数的检测和shift汇编函数的检测比较简单,因此放在了同一篇中。主要理解

  1. 自杀函数的风险。自杀函数可以做为一种隐藏的transfer的手段。
  2. shift汇编函数与其它语言的参数不一致,容易混淆。

1.自杀函数检测

自杀函数的风险与应用场景

Solidity中的自杀函数(selfdestruct)可以用于销毁智能合约,并将合约余额发送到指定的地址。尽管自杀函数在某些情况下可能是必要的,但它也存在一些潜在的风险和安全考虑,包括:

  1. 丧失合约控制权:一旦调用自杀函数销毁合约,将无法再对合约进行任何操作或更改。这意味着合约所有权和控制权将永久丧失。
  2. 无法撤销:自杀函数执行后,无法撤销或回滚操作。这可能会导致不可逆转的损失,特别是如果自杀操作是由错误或恶意行为触发的。
  3. 遗留的合约余额:自杀函数会将合约余额发送到指定的地址。如果地址是一个外部账户,而不是另一个智能合约,那么合约中的资金可能会永久丢失。
  4. 依赖性问题:如果其他合约或系统组件依赖于被销毁的合约,那么自杀操作可能会导致这些依赖性中断或无法正常工作。
  5. 代码执行问题:自杀函数的执行会导致合约停止,这可能会影响到合约中的其他功能或逻辑。

自杀函数有这么多的风险,那么自杀函数通常用在哪些场景中呢?

  1. 合约升级:当需要对智能合约进行升级或替换时,可以使用自杀函数来销毁旧合约并将资金和状态转移到新合约中。
  2. 资金退款:在某些情况下,可能需要将合约中的余额退还给合约的所有者或指定的地址。自杀函数可以用于将合约余额发送到指定的地址。
  3. 合约终止:当合约的目标已经达到或不再需要时,可以使用自杀函数来终止合约的执行并释放资源。
  4. 紧急情况处理:在发生紧急情况或安全漏洞时,可以使用自杀函数来停止合约的执行并防止进一步的损失或攻击。

自杀函数的检测逻辑

功能:检测合约代码中是否有对selfdestruct的调用。如果public或者external类型的函数名中包含suicide或者selfdestruct的关键字,就认为可能存在风险。代码会输出风险提示由人工进行分析排查。

检测逻辑:

  1. 遍历所有的合约中的所有的函数。对每个函数调用detect_suicidal_func(func)函数
  2. detect_suicidal_func(func)函数中,函数必须为public或者external,通过func.internal_calls取得函数内部调用的所有的函数对象。取函数对象的name属性就得到了函数名。这样就能得到当前函数中所有的被调用的函数名。
  3. 判断上面得到的被调用的函数名中是否有suicide(address)或者selfdestruct(address)

检测代码

def detect_suicidal_func(func):
        """Detect if the function is suicidal

        Detect the public functions calling suicide/selfdestruct without protection
        Returns:
            (bool): True if the function is suicidal
        """

        if func.is_constructor:
            return False

        if func.visibility not in ["public", "external"]:
            return False

        calls = [c.name for c in func.internal_calls]
        if not ("suicide(address)" in calls or "selfdestruct(address)" in calls):
            return False

        if func.is_protected():
            return False

        return True

扩展

  1. 关于自杀函数,还有一个比较有意思的应用是**Ethernaut Challenge — Level 7: Force 中,使用自杀函数强制向一个合约转账,有兴趣的可以检索看看。
  2. 还有个示例合约,合约好像是个猜数字的游戏,每次转入1 eth后就如果当前的余额是7,就赢了,就把所有的余额拿走。这个合约也有个漏洞,就是攻击可以通过sucide函数向该合约转入一定的eth,导致合约的判断逻辑出错,从而攻击合约。(具体记不太清了,只记得大概是这么个合约)

2.内置shift汇编函数调用错误

漏洞风险

内置的shift函数,第1个参数表示偏移的位数,第2个参数表示要操作的操作数

这点与其他语言中的shift函数参数的位置是相反的。这个差异可能导致shift代码的含义与要实现的功能不相符。

如下面的代码可能就是个有问题的代码。下面的代码中函数的意思将数字8右移a位。而代码作者的本意应该是把数字a右移8位。

contract C {
    function f() internal returns (uint a) {
        assembly {
            a := shr(a, 8)
        }
    }
}

检测逻辑

检测的脚本在detectors/assembly/shift_parameter_mixup.py脚本中。

检测逻辑:

  1. 定位到合约的每个函数。
  2. 函数对象中contains_assembly,在检测合约中是否包含assembly编码。
  3. 遍历函数对象的所有的node,遍历每个node中的每个ir。
  4. 如果ir是Binary对象类型,并且ir的类型为BinaryType.LEFT_SHIFT 或者 BinaryType.RIGHT_SHIFT,并且ir的variable_left 左边的变量为 Constant 类型。

主要的检测代码:

def _check_function(self, f):
        results = []

        for node in f.nodes:
            for ir in node.irs:
                if isinstance(ir, Binary) and ir.type in [
                    BinaryType.LEFT_SHIFT,
                    BinaryType.RIGHT_SHIFT,
                ]:
                    if isinstance(ir.variable_left, Constant):
                        info = [f, " contains an incorrect shift operation: ", node, "\n"]
                        json = self.generate_result(info)

                        results.append(json)
        return results

检测效果如图:

image.png

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
小驹
小驹
0xcD46...3461
weixin: xiaoju521区块链安全分析,欢迎私信沟通交流