xChar
·a year ago

Challenge #1 - Unstoppable

Unstoppable

合约

  • ReceiverUnstoppable:继承IERC3156FlashBorrower合约,用于发起闪电贷,执行闪电贷后的回调
  • UnstoppableVault:金库合约,继承IERC3156FlashLender、ERC4626,支持闪电贷

脚本

  • 依次部署DamnValuableToken、UnstoppableVault合约
  • 存入TOKENS_IN_VAULT数量的token到金库中,转入player用户INITIAL_PLAYER_TOKEN_BALANCE数目的token
  • 部署ReceiverUnstoppable合约
  • 执行攻击脚本
  • 期望ReceiverUnstoppable执行闪电贷的交易被revert

题解

攻击目标是使得通过ReceiverUnstoppable合约发起的executeFlashLoan方法被revert,首先分析executeFlashLoan的调用流程

image

重点在UnstoppableVault.flashLoan方法,分别会进行以下操作:

  • 计算闪电贷开始前的余额:totalAssets()
  • 计算当前的share:convertToShares(totalSupply)是否与前面计算出来的余额一致
  • 计算闪电贷手续费:flashFee
  • 转移amount个token到receiver,再调用receiver的onFlashLoan方法执行回调
  • 从receiver方转回amount+fee数目的token
  • 将fee转移给feeRecipient账户,完成本次闪电贷

若要使交易revert,关键的校验点在于使得:convertToShares(totalSupply) != totalAssets()

这两个函数都是ERC4626中的定义,关于此协议可参考下面的文章:
WTF-Solidity/51_ERC4626/readme.md at main · WTFAcademy/WTF-Solidity

简单来说就是ERC20的组合:资产代币asset和份额代币share,存入资产或提取资产时都会相对应的铸造或销毁对应数目的share代币

  • totalAssets():计算的是当前金库中的资产代币数目
  • convertToShares(totalSupply):totalSupply是总的share代币数目(只有deposit或mint时才会产生),convertToShares就是计算:assets * totalSupply / totalAssets()

要想使得两者不一致,只要不通过depost或mint方法向UnstoppableVault中转入token即可,因此攻击脚本内容如下:

it('Execution', async function () {
        /** CODE YOUR SOLUTION HERE */
        const dvtForPlayer  = token.connect(player);
        await dvtForPlayer.transfer(vault.address,1);
    });
Loading comments...