Damn Vulnerable Defi #1:Unstoppable 挑战

本文分析了Damn Vulnerable DeFi的Unstoppable挑战,该挑战基于ERC4626 Token Vault,目标是利用漏洞阻止vault提供flash loan。通过直接向vault合约转账少量token,破坏了totalAssets和totalSupply之间的平衡,从而使flashLoan功能失效,合约停止提供服务。

Damn Vulnerable Defi #1: Unstoppable

Damn Vulnerable DEFI: Unstoppable

这个挑战基于 ERC4626 Token Vault,其中 vault 以闪电贷(flashloan)的形式借给借款人,而闪电贷接收者必须支付一小笔费用。之前,我们已经解决了所有 ethernaut 挑战并撰写了一些解决方案文章。你可以在我的 medium 博客上找到。

Unstoppable 挑战目标:

有一个代币化的 vault,其中存入了 100 万个 DVT 代币。在宽限期结束之前,它提供免费的闪电贷。

为了在 100% 无许可之前发现任何错误,开发人员决定在测试网上运行一个 live beta。有一个监控合约来检查闪电贷功能的liveness。

从 10 个 DVT 代币的余额开始,证明有可能停止 vault。它必须停止提供闪电贷。

智能合约分析:

这个挑战中有两个合约,UnstoppableVault.solUnstoppableMonitor.sol。我们将主要关注 Unstoppable Vault 合约,因为漏洞就在这里。

contract UnstoppableVault is IERC3156FlashLender, ReentrancyGuard, Owned, ERC4626, Pausable {

......

function flashLoan(IERC3156FlashBorrower receiver, address _token, uint256 amount, bytes calldata data)
        external
        returns (bool)
    {
        if (amount == 0) revert InvalidAmount(0); // 提前失败
        if (address(asset) != _token) revert UnsupportedCurrency(); // 强制执行 ERC3156 要求
        uint256 balanceBefore = totalAssets();
        if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance(); // 强制执行 ERC4626 要求

        // 转出代币 + 在接收者上执行回调
        ERC20(_token).safeTransfer(address(receiver), amount);

        // 回调必须返回 magic 值,否则假定它失败了
        uint256 fee = flashFee(_token, amount);
        if (
            receiver.onFlashLoan(msg.sender, address(asset), amount, fee, data)
                != keccak256("IERC3156FlashBorrower.onFlashLoan")
        ) {
            revert CallbackFailed();
        }

        // 从接收者处提取 amount + fee,然后将 fee 支付给接收者
        ERC20(_token).safeTransferFrom(address(receiver), address(this), amount + fee);
        ERC20(_token).safeTransfer(feeRecipient, fee);

        return true;
    }

vault 合约中有一个 flashLoan 函数,每个人都可以为自己申请闪电贷。此函数有一些需要满足的必要条件:

  • amount 不应等于零值,否则交易会 revert。
  • token 地址应与 vault 合约持有的地址相同。
  • asset (totalAssest) 和 total share (totalSupply) 应该相等
  • 如果每个条件都满足,那么 vault 将闪电贷转移到接收者的地址
uint256 balanceBefore = totalAssets();
if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance(); // 强制执行 ERC4626

convertToShares 函数返回应该为接收者铸造多少 share 的 uint256 值。代币池和 share 池在 vault 合约中始终相等,它们永远不会被错误计算。

漏洞在于检查 totalAssests 和 totalSupply 是否相等的条件,如果不相等,则 revert 交易。记住,我们必须停止 vault 合约,这样任何人都无法claim闪电贷。

利用:

我们知道玩家有 10 个 DVT 来开始这个挑战,如果我们不使用任何函数而将少量代币直接转移到 vault 合约会发生什么?明白了吗?是的,这将破坏 vault 合约的核算系统,并在检查 totalAssets 和 totalSupply 的条件之间产生冲突。

function test_unstoppable() public checkSolvedByPlayer {
        ///利用:直接代币转账破坏了 vault 中的 share 计算
        token.transfer(address(vault), INITIAL_PLAYER_TOKEN_BALANCE);
    }

在 unstoppable 测试合约中运行此代码并解决挑战

这是我针对此挑战的完整解决方案代码,你可以 fork 我的 repo damn-vulnerbale-challenge-solutions-v4,并在本地使用 forge test 进行测试。

https://github.com/shubham562/damn-vulnerable-defi-v4-solutions/blob/master/test/unstoppable/Unstoppable.t.sol#L96C9-L96C70

  • 原文链接: blog.blockmagnates.com/d...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blockmagnates
blockmagnates
The New Crypto Publication on The Block