Boost _ Folks Finance 33269 - [Smart Contract - Critical] Logic flaw in UserLoanincreaseCollateral leads to double-counting of effectiveCollateral of userLoan
Submitted on Tue Jul 16 2024 17:44:11 GMT-0400 (Atlantic Standard Time) by @nnez for Boost | Folks Finance
Report ID: #33269
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
Protocol insolvency
Description
Summary
Malicious users can call a deposit function with amount=0 to make increaseCollateral
pushes the same poolId
into colPools
, causing getLoanLiquidity
to double-count the total collateral effective value of the loan.
Description
Users' loan position is stored in a mapping of a struct UserLoan
A member named colPools
stores a list of poolId
that users are using in their position.
This is analogous to a list of markets user enters in other lending platforms.
This list is used in a calculation of users' effective collateral value. See: https://github.com/Folks-Finance/folks-finance-xchain-contracts/blob/main/contracts/hub/logic/UserLoanLogic.sol#L283-L291 and https://github.com/Folks-Finance/folks-finance-xchain-contracts/blob/main/contracts/hub/logic/UserLoanLogic.sol#L230-L245
It iterates over colPool
to get all poolId
and uses them to retrieve current deposit balance of the loan for each pool
Below is the code snippet responsible for adding a new poolId
to user's loan.
See: https://github.com/Folks-Finance/folks-finance-xchain-contracts/blob/main/contracts/hub/logic/UserLoanLogic.sol#L20-L25
If the loan is currently holding 0 token, then it pushes a poolId
into colPools
.
However, this logic is flawed because the deposit amount of 0
is allowed. (I presume, since there is no validation preventing it).
See: https://github.com/Folks-Finance/folks-finance-xchain-contracts/blob/main/contracts/hub/LoanManager.sol#L74-L110 and https://github.com/Folks-Finance/folks-finance-xchain-contracts/blob/main/contracts/hub/logic/LoanManagerLogic.sol#L66-L151
Calling deposit function with amount=0 twice or more will cause increaseCollateral
to push the same poolId
into colPools
.
As a result, getLoanLiquidity
will retrieve a duplicate of poolId
and double or triple count the effective collateral value.
Impact
Adversaries can leverage double-counting (or more) to steal funds from the contract by borrowing more than their effective collateral value.
Proof of concept
Proof-of-Concept
I modify a test in LoanManager.test.ts
to demonstrate that calling deposit function with amount=0 will push the same poolId
into colPools
.
I use getUserLoan
to retrieve the current status of user's loan to show that colPools
stores duplicates of poolId
.
Note: getUserLoan
has the following interface:
Steps to reproduce
Set up a project as per protocol's README
Create a new file name
DoubleCounting.test.ts
in/test/hub/DoubleCounting.test.ts
with the code in this secret gist: https://gist.github.com/nnez/aa74df43103230004697438d4471c43fRun the test in the root of directory,
npx hardhat test test/hub/DoubleCounting.test.ts --grep "double-counting of collateral"
Expected result:
Last updated