Damn Vulnerable DeFi 解决方案——#5. The Rewarder 漏洞

本文分析了Damn Vulnerable DeFi V4第5题The Rewarder中的漏洞。该漏洞存在于claimRewards()函数中,由于合约在处理完所有claims后才标记为已领取,攻击者可以通过提交多个相同Merkle proof的claim,多次领取奖励,从而耗尽合约中的token。文章还提供了防止此漏洞的机制。

Damn Vulnerable DeFi V4 解决方案 — #5. The Rewarder

这个解释假设你事先了解此挑战中的智能合约,并将专门关注漏洞分析。

挑战概览

一个合约正在分发 Damn Valuable 代币和 WETH 的奖励。

要申领奖励,用户必须证明他们包含在选定的受益人集合中。但不用担心 gas。该合约已经过优化,允许在同一交易中申领多个代币。

Alice 已经申领了她的奖励。你也可以申领你的!但你已经意识到合约中存在一个关键漏洞。

从分发者处尽可能多地保存资金。将所有已追回的资产转移到指定的追回账户。

漏洞分析

获得资金的唯一方法是 claimRewards() 函数,所以让我们关注它。该函数允许我们通过一个请求来申领 DVT 和 WETH 奖励。

我们可以注意到的第一个漏洞是,该函数在两种情况下将我们的奖励标记为已申领:

  1. 在最后一次迭代期间

  2. 当要申领的代币发生变化时

此外,我们可以单笔交易中处理的申领数量没有限制。这创建了一个漏洞利用路径,我们可以在同一代币和批次上提交多个相同的申领(使用相同的有效 Merkle 证明,因为 ‘player’ 是合法的受益人)。由于合约仅在代币切换时或处理结束时才将申领标记为已申领,我们可以多次申领我们的奖励并耗尽合约。

攻击流程

  1. 读取分发 JSON 文件,以找到我们对 DVT 和 WETH 代币的合法申领金额

  2. 为同一代币和同一批次(批次 0)创建多个申领的数组

  3. 对于每个代币:

  • 使用有效的 Merkle 证明进行第一次申领(因为我们的地址在受益人名单上)

  • 使用相同的 Merkle 证明进行多次后续申领

  • 合约将验证每个申领的证明,但仅在处理完所有申领后才将批次标记为已申领

  1. 执行交易以耗尽分配器中的几乎所有代币

  2. 将所有已追回的资产转移到指定的追回帐户

预防机制

可以通过立即将申领标记为已申领而不是批量处理来防止此漏洞。合约应在继续下一个申领之前,以原子方式验证并完成每个申领。

    function claimRewards(Claim[] memory inputClaims, IERC20[] memory inputTokens) external {
        Claim memory inputClaim;
        IERC20 token;
        uint256 bitsSet; // accumulator
        uint256 amount;

        for (uint256 i = 0; i < inputClaims.length; i++) {
            inputClaim = inputClaims[i];

            uint256 wordPosition = inputClaim.batchNumber / 256;
            uint256 bitPosition = inputClaim.batchNumber % 256;

            if (token != inputTokens[inputClaim.tokenIndex]) {
                token = inputTokens[inputClaim.tokenIndex];
                bitsSet = 1 << bitPosition; // 设置给定位置的位
                amount = inputClaim.amount;
            } else {
                bitsSet = bitsSet | 1 << bitPosition;
                amount += inputClaim.amount;
            }

            bytes32 leaf = keccak256(abi.encodePacked(msg.sender, inputClaim.amount));
            bytes32 root = distributions[token].roots[inputClaim.batchNumber];

            if (!_setClaimed(token, inputClaim.amount, wordPosition, 1 << bitPosition)) revert AlreadyClaimed();
            if (!MerkleProof.verify(inputClaim.proof, root, leaf)) revert InvalidProof();

            inputTokens[inputClaim.tokenIndex].transfer(msg.sender, inputClaim.amount);
        }
    }

包含解决方案的 GitHub 存储库:https://github.com/HamMnatsakanyan/damn-vulnerable-defi-solutions/

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

0 条评论

请先 登录 后评论
CoinsBench
CoinsBench
https://coinsbench.com/