Challenge 3: Truster, Damn vulnerable defi V4 lazy solutions series
Table of contents
Why Lazy?
I’ll strongly assume that you’ve gone through challenge once or more time and you’ve some understandings of the challenge contracts flows. So, I’ll potentially will go towards solution directly.
Problem statement:
We are given 0 funds, and we have to rescue all funds from the TrusterLenderPool
.
There is only 1 smart contract TrusterLenderPool.sol which facilitates flashloan.
Easy peasy it is.
Vulnerability
flashLoan()
function code of TrusterLenderPool.sol smart contract is not following standard approach of flash loan implementation as it's not calling receiver's onFlashloan()
function.
Instead, it gives freedom to user to call whatever function they want on whatever smart contract from TrusterLenderPool
which is decided from provided data
. which leaves some vulnerability.
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;
}
The Attack Strategy
We'll take flashloan of 0 Eth and we'll pass data to approve all Eth available to attacker smart contract.
Solution
test/truster/TrusterExploiter.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.25;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {DamnValuableToken} from "../DamnValuableToken.sol";
contract TrusterLenderPool is ReentrancyGuard {
using Address for address;
DamnValuableToken public immutable token;
error RepayFailed();
constructor(DamnValuableToken _token) {
token = _token;
}
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;
}
}
test/truster/Truster.t.sol
function test_truster() public checkSolvedByPlayer {
TrusterExploiter trusterExploiter = new TrusterExploiter(address(token), address(pool), address(recovery));
require(
trusterExploiter.attack()
);
}
Let's see it in action,
forge test --mp test/truster/Truster.t.sol
Succeed!🔥💸
Incase if you need all solutions,
https://github.com/siddharth9903/damn-vulnerable-defi-v4-solutions
Subscribe to my newsletter
Read articles from Siddharth Patel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Siddharth Patel
Siddharth Patel
I'm Siddharth Patel, a Full Stack Developer and Blockchain Engineer with a proven track record of spearheading innovative SaaS products and web3 development. My extensive portfolio spans across diverse sectors, from blockchain-based tokenized investment platforms to PoS software solutions for restaurants, and from decentralized finance (DeFi) initiatives to comprehensive analytics tools that harness big data for global stock trends. Let's connect and explore how we can innovate together.