Skip to content
On this page

深入剖析Ethernaut DoubleEntryPoint:巧用Forta守护加密金库

在Web3世界的安全挑战中,OpenZeppelin的Ethernaut系列题目一直是智能合约学习者和安全研究员的试金石。今天,我们将深入探讨其中一个巧妙的关卡——DoubleEntryPoint。这个挑战不仅考验你对Solidity合约交互的理解,更引入了去中心化监控网络Forta的概念,教你如何利用它来保护你的数字资产金库。

挑战背景:CryptoVault与它的代币

想象你有一个安全的CryptoVault(加密金库)合约,它持有两种不同的代币:

  1. DoubleEntryPointToken (DET):这是金库的核心“底层代币”,对金库的运作至关重要,绝不能被扫走
  2. LegacyToken (LGT):这是另一种代币,用于其他用途。金库有一个sweepToken函数,旨在清理合约中意外滞留的任何非底层代币,并将它们安全地发送到指定地址。

规则很明确:你可以通过sweepToken函数清理LGT,但绝不能触碰DET。金库最初持有100单位的DET和100单位的LGT。我们的任务是找出CryptoVault中的潜在漏洞,并利用Forta网络防止金库中的代币被盗取。

“双重入口”的秘密:LegacyToken的委托机制

问题的关键在于LegacyToken的特殊设计。LGT合约自身继承了ERC20标准,但它有一个巧妙的后门:delegateToNewContract函数。这个函数允许LGT将其transfer(转账)功能的执行权委托给另一个合约

在本例中,LGTtransfer可以被设置为委托给DoubleEntryPointToken(DET)合约的delegateTransfer函数。这就像LGT成为了一个入口,但实际的转账操作却由DET来完成,故称之为“双重入口”。

DET合约的delegateTransfer函数具有两个重要修饰符:

  • onlyDelegateFrom:确保只有LegacyToken合约才能调用此函数。
  • fortaNotify:这是我们防御的关键。

Forta网络:链上警报系统

DoubleEntryPoint挑战引入了Forta网络。Forta是一个去中心化的、社区驱动的监控网络,用于检测Web3系统中的威胁和异常。DoubleEntryPointToken合约通过fortaNotify修饰符集成了Forta的通知机制:

DETdelegateTransfer函数被调用时,fortaNotify修饰符会首先执行以下操作:

  1. 通知Forta:它会调用Forta合约的notify函数,进而触发我们注册的DetectionBot合约的handleTransaction函数。
  2. 检查警报:在完成原始交易逻辑(即实际的_transfer操作)之后,fortaNotify会检查Forta网络中是否有我们的机器人发出警报(raiseAlert)。
  3. 回滚交易:如果我们的机器人发出了警报,DET合约将立即回滚整个交易,并抛出"Alert has been triggered, reverting"的错误。

潜在的漏洞与我们的防御策略

这里的漏洞在于:CryptoVaultsweepToken函数在执行时,仅仅检查了传入的token参数是否不等于underlying(即DET)。由于LGT合约的地址与DET合约的地址不同sweepToken(LGT)的检查将顺利通过。金库会自信地调用LGT.transfer(),期望清理掉LGT。

然而,如果此时LGT的转账已经被恶意地或被事先设置为委托给了DET,那么这次“清理”LGT的操作,实际上会触发DETdelegateTransfer函数及其内含的fortaNotify安全网。如果无人监控,即使攻击者无法直接扫走DET,他们也能通过此路径导致一些意想不到的行为或绕过其他保护机制。

我们的任务就是利用这个强大的Forta安全网,防止金库被意外清空。

我们的解决方案:Forta检测机器人

我们的任务就是编写一个智能的DetectionBot,并将其注册到Forta网络中。这个机器人需要做的非常简单,但却极其有效:一旦handleTransaction函数被调用(这意味着有交易经过DETdelegateTransfer并触发了fortaNotify),它就立即向Forta报告一个警报(raiseAlert)。

以下是我们的Hack.sol机器人的代码:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./DoubleEntryPoint.sol"; // 导入Forta接口

contract Hack is IDetectionBot {
    IForta private immutable target;

    // 构造函数:记录Forta合约的地址
    constructor(address _target) {
        target = IForta(_target);
    }

    // handleTransaction 函数:当Forta通知时被调用
    function handleTransaction(address user, bytes calldata msgData) external {
        // 确保是Forta合约调用,增加安全性
        require(msg.sender == address(target), "Unauthorized");
        
        // 立即向Forta网络发出警报!
        target.raiseAlert(user);
        
        // msgData 可以用于分析交易数据,本例中不作具体分析
        msgData; 
    }
}

当我们的机器人成功注册并被DETfortaNotify修饰符触发时,它会立刻发出警报。由于DET合约在fortaNotify修饰符中加入了if (forta.botRaisedAlerts(detectionBot) > previousValue) revert("Alert has been triggered, reverting");的逻辑,任何试图通过LGT委托转账来触发DET的交易,都将被强制回滚。

这样一来,即使CryptoVault尝试sweepToken(LGT),在LGT转账被委托给DET的情况下,交易也会因为我们的Forta机器人发出警报而被回滚,从而保护了金库,使得任何形式的意外扫荡都无法成功。

演示与结果

在提供的测试用例98_test_double_entry_point.ts中,我们可以清晰地看到整个防御过程:

  1. 我们首先部署了Hack合约,获得了我们的检测机器人。
  2. 然后,我们将这个机器人的地址注册到Forta合约中(fortaContract.setDetectionBot(HACK_ADDRESS))。
  3. 最后,当测试尝试调用cryptoVaultContract.sweepToken(legacyTokenAddr)时,它会如预期般地失败,并抛出"Alert has been triggered, reverting"的错误。
  4. 验证金库中DoubleEntryPointToken的余额仍然大于零,表明核心资产被成功保护。

总结

DoubleEntryPoint挑战精妙地展示了在复杂智能合约交互中潜在的安全风险,以及如何利用去中心化监控网络Forta作为一道额外的防线。这不仅仅是一个简单的漏洞修复,更是一种对合约设计模式、委托调用机制以及链上监控工具的深刻理解。它提醒我们,在设计和部署智能合约时,必须考虑所有可能的交互路径,并为它们构建多层防御,确保数字资产的安全。

Built with AiAda