Boost _ Folks Finance 33869 - [Smart Contract - Medium] loanIds are easy to reproduce and front-running enable malicious parties to lock user funds
Submitted on Wed Jul 31 2024 14:44:48 GMT-0400 (Atlantic Standard Time) by @jovi for Boost | Folks Finance
Report ID: #33869
Report type: Smart Contract
Report severity: Medium
Target: https://immunefi.com/
Impacts:
Permanent freezing of funds
Description
Brief/Intro
When creating a loan and a deposit at a single transaction, users can arbitrarily select the loanId as an argument for the createLoanAndDeposit function at the SpokeToken contract:
This loanId can be observed by mempool watchers and front-run in order to induce transaction reversion when the bridge adapters attempt to receive the message to create the loan at the target chain.
Vulnerability Details
When creating a loan and a deposit, a user can pick any loan id as he/she desires. However, any other user can call the same function with the same loanId but with a different amount of assets.
The issue with that lies at the LoanManager's createUserLoan function at the Hub chain, as it reverts if the loanId has already been created:
So, if 1 user attempts a createLoanAndDeposit function call and sends some assets he/she is always at risk at being front-run and locking the assets without clear pathways for recovery.
Impact Details
Users that create new loans with deposits may end up having their funds stuck if they are front-run.
In case the bridge logic is Chainlink's CCIP, the funds will be irrecoverable, as both the TokenAdapter and DataAdapter are non-upgradeable and Chainlink's manual execution (as a manner to attempt to receive the message again) will keep reverting. Since the adapters also don't have retryFailedMessage
implementations, the funds are stuck.
As the TokenAdapter and DataAdapter contracts do not gracefully handle errors by calling the actions inside ccipReceive in try/catch blocks, there will be no other mechanism to attempt running the message again at the Hub chain.
The SpokeToken contracts don't have built-in mechanisms to return the funds to users in case the message fails at the target chain. Therefore users may lose their funds with no chance of recovery.
References
Transfer Tokens with Data - Defensive Example | Chainlink Documentation
Mitigation
The usage of hash-based loan IDs may fix the issue, as the one-way cryptographic property may make it impossible to reproduce loan IDs. Consider the following solution:
Proof of concept
Proof of concept
Hub chain: Avax Spoke chain: ETH
Alice is currently in ETH and wants to create a loan and deposit 100e18 worth of tokenA at Avax.
She calls the SpokeToken createLoanAndDeposit function with loanId set to 0x01.
Bob observes the mempool and front-runs her call by creating a loan and deposit with 1e18 worth of tokenA and loanId = 0x01.
Since Bob has front-run Alice, CCIP router picks up Bob's message first and creates the loan at the Hub chain for him.
The CCIP router then attempts to create a loan for Alice, but since a loan with equal ID already exists, it reverts and the message is lost.
The following test can also be placed at the Hub.test.ts file, but it will not revert at the final condition as the MockLoanManager contract doesn't implement any logic but to emit the CreateUserLoan event:
It can be run with the following command from the main folder:
Last updated