# Damn Vulnerable DeFi: Truster 

By [blokboy](https://paragraph.com/@blokboy) · 2021-10-05

---

_Warning: Solution to the challenge will be discussed here. Also note, I could be totally fucking wrong about everything I’m about to write. This is mostly just me long form “tweeting into the ether” about my smart contract journey. Proceed with caution._

Problem
-------

> _More and more lending pools are offering flash loans. In this case, a new pool has launched that is offering flash loans of DVT tokens for free._
> 
> _Currently the pool has 1 million DVT tokens in balance. And you have nothing._
> 
> _But don’t worry, you might be able to steal them all from the pool._

This problem looks like it’s expecting us to drain the lender completely and walk away with a bunch of (or all of) the DVT tokens.

> TrusterLenderPool.sol

![An image of the TrusterLenderPool smart contract](https://storage.googleapis.com/papyrus_images/bcc3d4222925e053951f69df3f445270793348c2f4fce04f13fe1f773ad9e283.png)

An image of the TrusterLenderPool smart contract

This seems like it should’ve been the first problem posted! I could be wrong, but it seems like the issue here almost immediately jumps out `target.call(data)` , but I know the story of Icarus too well, so we are going to go through it anyways. Per usual, we see that OpenZeppelin at the top of the board, providing us the ERC20 Interface and the ReentrancyGuard which doesn’t really matter here I don’t think because we aren’t going to exploit this contract with reentrancy. We’re going to do it with the ERC20 Interface that’s pointing at the smart contract that manages the DVT tokens 😵.

### constructor

When the `TrusterLenderPool`contract is initially deployed we are just going to create a reference to the contract address passed in by the person deploying, in this case, we’re expecting `tokenAddress` to point to the contract address that holds all of the DVT tokens.

> Sidenote: I’m going to go on a small tangent about ERC20 standard and why I think it’s important to this particular exploit.
> 
> **totalSupply()** - how many total tokens will be created and managed within this contract (also can think of this as the sum of all token balances)
> 
> **balanceOf(address \_owner)** - how many tokens does the supplied address own
> 
> **transfer(address \_to, uint256 \_value)** - take the number of tokens specified in `_value` and record that value as being available for the address `_to` and it’s very important to remember that these are just state changes, no tokens are actually _moving_ anywhere
> 
> **transferFrom(address \_from, address \_to, uint256 \_value)** - same logic as the `transfer` method but instead of assuming that the state change is between the smart contract managing the ERC20 token it’s assuming that this is a value transfer happening between two actors where **_neither_** is the smart contract managing the ERC20 tokens (this is like sending tokens to your friends or maybe another contract where you manage your holdings across multiple ERC20 contracts)
> 
> **approve(address \_spender, uint256 \_value)** - in order to actually use the tokens you must call this function with the address of the account we want to authorize to spend from the DVT contract as well as specifying the actual amount we would like to approve for extraction. It’s like going to the bank and telling the teller, “Hey, I know I have $300 in my bank account, but I’d like you to reject any attempt to spend more than $100”. (This is how we exploit the contract, I believe but more on that later).
> 
> **allowance(address \_owner, address \_spender)** - allowance is the function that tells you just how much a particular account has been approved to pull from a particular users wallet or smart contract. In this case our `_owner` is the DVT contract and our `_spender` would be us. It’s also important to note that `allowance` and `balanceOf` can be mismatched.
> 
> One final thing to always remember about using ERC20 standards (and generally any standard) is that every single function is **_public and callable by anyone._**

### flashLoan

I don’t want to be too arrogant with this, but I’m increasingly convinced that the call data is the vulnerability here. The f(x) takes in 4 parameters for extending a flash loan, namely:

> `uint256 borrowAmount` (how much money are we asking for with this loan)
> 
> `address borrower` (who is requesting the loan)
> 
> `address target` (who are we wanting to execute the `call` on)
> 
> `bytes calldata data` (what information do we want to send to target to perform the function)

This f(x) is again non reentrant and it requires an `external` accessor. Ok, cool. Immediately, the contract is going to check to see if the balance available in the contract is sufficient to satisfy the requested `borrowAmount` at which point we will automatically call the `transfer` method which will “send” the requested `borrowAmount` to our `borrower` and this sets us up for the trick. In line 27, we see that we attempt to have the `target` call a function with the `data` provided when the function is called.

> Sidenote: **calldata** (special data location that contains the function arguments, only available for external function call parameters), is usually used to encode functions for talking directly to the EVM. Which is this case is an indication that we should be sending an encoded function definition for the `target` to satisfy

So for this piece, we can’t just take the money immediately in the first run because the `flashLoan` f(x) is checking to make sure that the balance is restored at the end of the the call or all changes will end up being reverted and no one makes any money and now you’ve spent gas for nothing! So, what we can do here is instead of trying to steal the tokens, we can `approve` our address for the total balance of the DVT contract and circle back after our approval is successfully made and `transfer` all of the DVT tokens to myself.

> Sidenote: our `balanceOf` will be `0` when the function is done running but our `allowance` will be the total supply of tokens currently in the pool.

solution
--------

We can use `web3` or `ethers` libraries for creating our encoded f(x) which is simple enough it just allows for telling the EVM the exact bytecode for the function we’re looking for and the arguments we’d like to call it with (if any).

![The top level view of the truster.js test file using web3](https://storage.googleapis.com/papyrus_images/d64d978c3af6c9cb7071a16c72c6869ad5e94513077b6292448a974399f8bb50.png)

The top level view of the truster.js test file using web3

![The top level view of the truster.js test file using ethers](https://storage.googleapis.com/papyrus_images/377573325f97191c2c98b21fc602a4437e546c3539199d2fbb6b40612b6aa1f6.png)

The top level view of the truster.js test file using ethers

![The Exploit function within tester.js file using web3](https://storage.googleapis.com/papyrus_images/155b2f68329a334e9f0651c6c9e082ee20e1ee1e4512d293bbd83eeb6f9decd3.png)

The Exploit function within tester.js file using web3

![The exploit function within tester.js file using ethers](https://storage.googleapis.com/papyrus_images/7b7b4dbb95a8b324ae4b0373621907b823b86c4c7427f0946f10a8dd188f3f23.png)

The exploit function within tester.js file using ethers

The `encodeFunctionCall`is pretty standard, we are defining the entire function call with parameters **_and_** signature. The array at the end supplies the variables we’d like to encode and pass in as our `inputs` mapping `attacker` to the `spender` and `TOKENS_IN_POOL` to `amount` and then we call `flashLoan` supplying nothing for the `borrowAmount`, so we don’t have to pay anything back to satisfy the balance checks made in the contract, we want to set our `attacker` as the `borrower` and our `target` that we want to make the function call is the address for our DVT smart contract and finally we pass in our `data` which is our manually constructed f(x) call to have the DVT contract call `approve` on itself with `attacker` in place of `spender` and `TOKENS_IN_POOL` being the total amount we are approved to transfer out in the next line with `transferFrom` — and just like that we’ve drained it.

### reflection

The main thing here is just being careful with the passing in `calldata` as well as being careful about specifying who can actually call a function and who they can have call the function. There wasn’t really any reason I can see to even include a `target` here within the `flashLoan` piece it wasn’t really necessary to enable the sending of the loans, and even more so it didn’t even really matter who we set as the `borrower` since `attacker` is being mapped to `spender` within `data` , so in theory we could’ve called `flashLoan` with any address we wanted as `borrower` and it would have no bearing on the success of this exploit as long as the proper address for the attacker is encoded in `data.`

### conclusion

This was a bit clearer for me to solve, but I’m not sure if it’s because I’m getting better or if I just happened to already have the requisite knowledge of encoding function calls to see a clear error here. Whenever I see flash loans in the wild, I typically see a calldata piece that allows users to specify function calls but this seems like something you’d want to do from the `borrower` side and not the DVT smart contract side, so that we aren’t having the DVT contract calling public functions on itself like that, but I’m going to spend a lot more time investigating the positive use cases for a pattern like this and how to better defend against it.

---

*Originally published on [blokboy](https://paragraph.com/@blokboy/damn-vulnerable-defi-truster)*
