Unstoppable
分析
题目要求:
There’s a lending pool with a million DVT tokens in balance, offering
flash loans for free.If only there was a way to attack and stop the pool from offering
flash loans …You start with 100 DVT tokens in balance.
题目给了一个闪电贷合约,要求我们停止这个闪电贷合约继续运行。
闪电贷合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
interface IReceiver {
function receiveTokens(address tokenAddress, uint256 amount) external;
}
/**
* @title UnstoppableLender
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract UnstoppableLender is ReentrancyGuard {
IERC20 public immutable damnValuableToken;
uint256 public poolBalance;
constructor(address tokenAddress) {
require(tokenAddress != address(0), "Token address cannot be zero");
damnValuableToken = IERC20(tokenAddress);
}
function depositTokens(uint256 amount) external nonReentrant {
require(amount > 0, "Must deposit at least one token");
// Transfer token from sender. Sender must have first approved them.
damnValuableToken.transferFrom(msg.sender, address(this), amount);
poolBalance = poolBalance + amount;
}
function flashLoan(uint256 borrowAmount) external nonReentrant {
require(borrowAmount > 0, "Must borrow at least one token");
uint256 balanceBefore = damnValuableToken.balanceOf(address(this));
require(balanceBefore >= borrowAmount, "Not enough tokens in pool");
// Ensured by the protocol via the `depositTokens` function
assert(poolBalance == balanceBefore);
damnValuableToken.transfer(msg.sender, borrowAmount);
IReceiver(msg.sender).receiveTokens(address(damnValuableToken), borrowAmount);
uint256 balanceAfter = damnValuableToken.balanceOf(address(this));
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
}
}
合约拥有两个函数,并使用了名为“DVT”的ERC20代币。
DVT代币合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title DamnValuableToken
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract DamnValuableToken is ERC20 {
// Decimals are set to 18 by default in `ERC20`
constructor() ERC20("DamnValuableToken", "DVT") {
_mint(msg.sender, type(uint256).max);
}
}
合约创造之初就给了创造者最多的代币,而我们需要让这些代币失效,也就是不能进行使用。
UnstoppableLender.sol合约中定义了两个函数,depositTokens和flashloan,前者用来向合约增加代币,后者则是闪电贷函数,其中又限定了调用者满足IReceiver接口,因此借贷者也应该是一个合约,题目也给出了借贷者的合约。
借贷者合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../unstoppable/UnstoppableLender.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title ReceiverUnstoppable
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract ReceiverUnstoppable {
UnstoppableLender private immutable pool;
address private immutable owner;
constructor(address poolAddress) {
pool = UnstoppableLender(poolAddress);
owner = msg.sender;
}
// Pool will call this function during the flash loan
function receiveTokens(address tokenAddress, uint256 amount) external {
require(msg.sender == address(pool), "Sender must be pool");
// Return all tokens to the pool
require(IERC20(tokenAddress).transfer(msg.sender, amount), "Transfer of tokens failed");
}
function executeFlashLoan(uint256 amount) external {
require(msg.sender == owner, "Only owner can execute flash loan");
pool.flashLoan(amount);
}
}
该合约只有两个函数,一个函数用来将借到的通证返还给闪电贷合约,另一个函数用来向闪电贷合约借通证,并限制借贷者只能是owner。
我们来分析一下闪电贷合约中的几个限制条件的含义分别是什么。
-
require(borrowAmount > 0, "Must borrow at least one token");
此项防止无意义的借贷,确保每次运行确实进行借贷了 -
require(balanceBefore >= borrowAmount, "Not enough tokens in pool");
此项是确保有足够的通证余额进行借贷 -
assert(poolBalance == balanceBefore);
此项是为了保证合约中现在的通证,与之前的通证相同 -
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
此项是为了确保借贷者合约将通证进行了返还
我们注意到函数中的第三个限制条件,balanceBefore是合约现在的余额,而poolBalance是合约之间的余额,并且这个变量只在depositToken中进行了修改,但是我们并不仅仅只有这一种方法为合约增加通证,我们从这一点切入进行攻击。
攻击
攻击代码:
it('Exploit', async function () {
/** CODE YOUR EXPLOIT HERE */
await this.token.transfer(this.pool.address,1)
});
我们仅仅是在攻击区域增加了 await this.token.transfer(this.pool.address,1),为合约发送了一个DVT通证,这时合约的poolBalance并没有发生变化,但余额其实增加了1DVT,所以一旦想要再次借贷,poolBalance和balanceBefore并不相等,无法正常借贷。
攻击完成
Naive Receiver
分析
题目要求:
There’s a lending pool offering quite expensive flash loans of Ether,
which has 1000 ETH in balance.You also see that a user has deployed a contract with 10 ETH in
balance, capable of interacting with the lending pool and receiveing
flash loans of ETH.Drain all ETH funds from the user’s contract. Doing it in a single
transaction is a big plus文章来源:https://www.toymoban.com/news/detail-671217.html
大意就是有一个高利贷的闪电贷合约,借贷池中有一千个eth,而用户拥有十个eth,我们要做的就是将用户的这十个ETH掏空。让我们来看看合约
闪电贷合约:文章来源地址https://www.toymoban.com/news/detail-671217.html
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title NaiveReceiverLenderPool
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract NaiveReceiverLenderPool is ReentrancyGuard {
using Address for addre
到了这里,关于Damn Vulnerable DeFi靶场实战(1-5)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!