Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Trader Joe v2 contest Findings- 1080

Code4renaPosted 3 Years Ago
  • Code4rena is a crowd sourced security audit platform. Recently, Trader Joe V2 received a security audit. Joe V2 is a decentralized exchange based on Liquidity Book, an AMM protocol.
  • The first major vulnerability was bad book keeping because of temporary variables. The code snippet is very short:
    uint256 _fromBalance = _balances[_id][_from];
    uint256 _toBalance = _balances[_id][_to];
    
    _balances[_id][_from] = _fromBalance - _amount;
    _balances[_id][_to] = _toBalance + _amount;
    
  • What's the big deal? There's no verification on to being the same as from. Walking through the values if they are the same with the starting balance being 10 and the amount being 10:
    1. _fromBalance = 10
    2. _toBalance = 10
    3. _balances[_id][_from] = 10 - 10 (_fromBalance)
    4. _balances[_id][_to] = 10 + 10(_toBalance)
  • The fourth line of code does not take into consideration the third line of code. Hence, the amount of funds in the account cane be infinitely doubled. The second high finding was an incorrect calculation between V1 and V2 pools. This incorrect calculation could have led to funds being lost for the user - but not stolen; both 3 and 4 were similar to this as well. Weird to me this is considered a security finding.
  • Trader Joes is similar to most LP pools; a user can call mint() to provide liquidity and get an LP token in return. Additionally, it can call burn() to return the LP token to get their underlying assets as well as call collectFees to get the fees they are owed.
  • The fees are calculated using the debt model. In other words, it is keeping track of the last time the fees were obtained by the user and subtracting this from amount. This is to ensure that the user doesn't collect fees for the entire growth of the token; they should only get the fees for the time they have been active.
  • When calling a multitude of functions for token swaps (mint, burn, transfer), the hook _beforeTokenTransfer is called to update the user debts. The hook only updates the cache fees via _cachefees() if the address is not 0 and the address does not equal the address of the pool. This was done since the contract is never expected to own the LP tokens.
  • So, what's the bug? The code for _cacheFees is never triggered! This means that the time we got in (debt) does NOT get added to the calculations. When calling mint, the recipient of the token can be chosen. Because of further bad bookkeeping, the funds can be taken out as well.
  • Steps for the exploit:
    1. Transfer funds to the address to mint in the next step.
    2. mint with the recipient being the contract itself.
    3. Call collectFees() with pair address as the account. It will send itself the fees. Now, the token believes that the user sent it money, even though the contract paid itself! This is because of it keeps an internal balance and comparing that to the actual balance of it. This difference convinces the protocol that we indeed set money to it!
    4. Call swap to collect all of the fees.
  • There is an integer overflow within an unchecked block that allows for the fees calculation to go rouge. This allows the attacker to steal the entire reserves, instead of the total fees accrued only. I absolutely love this bug! Super crazy attack which starts by sending your money away.