#チャレンジ#1 - 止まらない
コントラクト#
- ReceiverUnstoppable:IERC3156FlashBorrower コントラクトを継承し、フラッシュローンを発行し、フラッシュローン後のコールバックを実行するためのものです。
- UnstoppableVault:フラッシュレンダー、ERC4626 を継承した金庫コントラクトで、フラッシュローンをサポートしています。
スクリプト#
- DamnValuableToken、UnstoppableVault コントラクトを順番にデプロイします。
- TOKENS_IN_VAULT の数のトークンを金庫に入れ、INITIAL_PLAYER_TOKEN_BALANCE の数のトークンをプレイヤーユーザーに転送します。
- ReceiverUnstoppable コントラクトをデプロイします。
- 攻撃スクリプトを実行します。
- ReceiverUnstoppable がフラッシュローンのトランザクションを revert することを期待します。
解決策#
攻撃の目標は、ReceiverUnstoppable コントラクトを介して実行される executeFlashLoan メソッドが revert されることです。まず、executeFlashLoan の呼び出しフローを分析します。
重要なのは、UnstoppableVault.flashLoan メソッドで、次の操作が行われます。
- フラッシュローン前の残高を計算する:totalAssets ()
- 現在のシェアを計算する:convertToShares (totalSupply) が前の計算で得られた残高と一致するかどうか
- フラッシュローン手数料を計算する:flashFee
- amount 個のトークンをレシーバーに転送し、レシーバーの onFlashLoan メソッドを呼び出してコールバックを実行する
- amount + fee の数のトークンをレシーバーから転送する
- fee を feeRecipient アカウントに転送して、フラッシュローンを完了する
トランザクションを revert させるためには、convertToShares(totalSupply) != totalAssets()
となるようにする必要があります。
これらの関数は、ERC4626 で定義されているものであり、このプロトコルについては以下の記事を参照してください:
WTF-Solidity/51_ERC4626/readme.md at main · WTFAcademy/WTF-Solidity
簡単に言えば、ERC20 の組み合わせであり、アセットトークンとシェアトークンの組み合わせです。アセットを預け入れたり引き出したりすると、対応する数のシェアトークンが鋳造または破棄されます。
totalAssets()
:現在の金庫のアセットトークンの数を計算します。convertToShares(totalSupply)
:totalSupply は総シェアトークンの数です(預け入れまたは鋳造のみの場合にのみ発生します)。convertToShares は、assets * totalSupply /totalAssets () を計算するものです。
これらの 2 つが一致しないようにするには、UnstoppableVault にトークンを預け入れるための depost または mint メソッドを使用しないだけです。したがって、攻撃スクリプトの内容は次のようになります:
it('Execution', async function () {
/** CODE YOUR SOLUTION HERE */
const dvtForPlayer = token.connect(player);
await dvtForPlayer.transfer(vault.address,1);
});