Boost _ Folks Finance 33780 - [Smart Contract - Critical] Zero deposits can be used to artificially
Submitted on Mon Jul 29 2024 08:52:14 GMT-0400 (Atlantic Standard Time) by @JCN2023 for Boost | Folks Finance
Report ID: #33780
Report type: Smart Contract
Report severity: Critical
Target: https://testnet.snowtrace.io/address/0xf8E94c5Da5f5F23b39399F6679b2eAb29FE3071e
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Brief/Intro
A malicious user can leverage null deposits to artificially expand the number of their open collateral positions for a specific poolId. They can then perform a valid deposit, which will result in their total collateral value equaling actual_deposit * num_open_positions_poolId. This allows the malicious user to then borrow an excessive amount since their collateral value has been inflated. With proper inputs and enough capital (or leveraging flashloans), the malicious user can steal a large amount of funds from any pool (funds stolen will be capped by the borrow cap for each pool).
Bug Description
Ignoring cross-chain components, the execution flow for deposits are as follows: SpokeToken::deposit -> router/adapter/hub interactions -> LoanManager::deposit -> LoanManagerLogic::executeDeposit -> UserLoanLogic::increaseCollateral.
The bug exists in the last step of the above flow:
UserLoanLogic::increaseCollateral
As we can see above, the increaseCollateral function does not validate the fAmount. Therefore, this fAmount can be equal to 0 and if the user has not performed an actual deposit yet then line 22 will expand the user's collateral positions array by pushing the poolId to the array. A user can therefore perform multiple null deposits in order to push the poolId to their collateral positions array multiple times. The user can then perform a valid deposit, which will increase their collateral balance on line 24.
To understand how this enables the user to exploit the protocol we will look at the health check that is performed at the end of a borrow operation:
LoanManagerLogic::executeBorrow
UserLoanLogic::isLoanOverCollateralized
UserLoanLogic::getLoanLiquidity
As we can see above, the getLoanLiquidity function will iterate over all of the open collateral positions for the user's loan and then sum the user's balance for each poolId seen in the array. If the user had previously performed 10 null deposits (prior to their actual deposit for poolId), then the same poolId will have been added to the array 11 times. Therefore, the user's collateral value for the poolId will effectively be multiplied by 11. This will allow the user to borrow much more than they should be able to.
Impact
A malicious user can steal funds from any pool. However, note that the amount they can steal is bounded by the configured borrow cap for the pool.
Recommended Mitigation
I would recommend requiring deposits to be greater than 0.
Proof of concept
For simplicity, I have chosen to showcase how a user can drain a pool on the Hub chain by initiating operations on the Hub chain itself via the HubAdapter, and therefore all actions occur on one chain. However, note that a malicious user is able to perform this exploit via cross-chain operations as well.
To run foundry POC:
add test file to
test/directory of a foundry repoadd
AVAX_FUJI_RPC_URLvariable as environment var or in.envfilerun test with
forge test --mc FolksPOC_StealFunds -vvv
Output from test:
Last updated
Was this helpful?