Full Report
Flash loans, or the borrowing of a large amount of money within a single transaction, work because they must be repaid with interest by the end of the transaction. In EVM, an external call from the context of the smart contract can be made with a callback to a user-controlled contract. In Solana, Cross-Program Invocations (CPI) make this too hard to do. Therefore, instruction introspection is used to ensure that the flash loan is indeed paid by examining the instructions that are supposed to be executed in the future. The instruction sysvar account makes this possible. MarginFi is a lending platform on Solana but it adds some extra flexibility to this. The account MarginfiAccount is used to track users' assets and liabilities. It must remain healthy at all times except in the case that a flash loan has been created for it. In this case, the health check is skipped, assuming that everything will be resolved by the end of the call. When creating a flashloan, the lending_account_start_flashloan function will ensure that there's a following call to lending_account_end_flashloan. Using this call, a health check is performed to ensure that the funds from the flash loan have been returned. Recently, a new instruction called transfer_to_new_account was created. This is for migrating the original MarginfiAccount to a fresh account and empties the original one. This call fails to ensure that we're not in the middle of a flash loan though! This leads to the following attack: Create an account A with a flash loan. Borrow the maximum amount of funds for the flash loan. Call transfer_to_new_account to move the outstanding liabilities from A to a new account B. End the loan on account A. Since the liabilities are now good and the call to lending_account_end_flashloan is made, this is sufficient. End the call and never repay the funds. Now, account B contains a flash loan account that has never been paid back. A solid find from Felix! A good lesson for me is that even small changes in functionality can have horrible consequences. The more functionality you have, the more these can interact and cause havoc with each other.
Analysis Summary
# Vulnerability: Unenforced Repayment Check During MarginFi Account Migration
## CVE Details
- CVE ID: Not assigned (Private disclosure/fix)
- CVSS Score: Not assigned (Severity inferred as Critical based on context)
- CWE: CWE-824: Improper Restriction of Operations within the Bounds of Memory (or a related logic flaw concerning state management)
## Affected Systems
- Products: marginfi (lending platform on Solana)
- Versions: Versions utilizing the `marginfi-v2` smart contract logic prior to the fix, specifically those including the `transfer_to_new_account` instruction without proper state validation.
- Configurations: Any configuration using the platform's flash loan functionality combined with account migration via `transfer_to_new_account`.
## Vulnerability Description
The MarginFi platform uses an `ACCOUNT_IN_FLASHLOAN` flag to disable standard health checks during a flash loan, assuming the funds will be repaid by a designated instruction before the transaction concludes. The loan must start with `lending_account_start_flashloan` and end with `lending_account_end_flashloan` (which performs the health check/repayment verification).
A newly introduced instruction, `transfer_to_new_account`, was created to migrate a user's tracking account (`MarginfiAccount`) to a fresh account, emptying the original. This instruction **failed to check** if the `ACCOUNT_IN_FLASHLOAN` flag was set on the source account.
An attacker could:
1. Start a flash loan on `Account A`, setting the `ACCOUNT_IN_FLASHLOAN` flag and borrowing the maximum amount.
2. Call `transfer_to_new_account` to move the *liabilities* (tracked via the margin account state) from `A` to a new, empty `Account B`.
3. Call `lending_account_end_flashloan` on `Account A`. Because the state showing outstanding liabilities was moved, `Account A` might appear healthy (or the liability tracking is effectively bypassed on A), allowing the repayment check to succeed or be rendered irrelevant on $A$.
4. The flash loan is considered finished on $A$, but the borrowed funds were never repaid.
5. `Account B` now holds the debt (and potentially the borrowed funds via subsequent operations) from a loan that was never concluded or repaid on either account.
This results in a net gain of borrowed funds without repayment.
## Exploitation
- Status: Contained (Disclosed privately; no funds lost or at risk post-patch)
- Complexity: Medium to High (Requires deep understanding of Solana instruction ordering and MarginFi state management)
- Attack Vector: Network (Sending a crafted transaction)
## Impact
- Confidentiality: None
- Integrity: Critical (Allows unauthorized draining of protocol liquidity via unbacked loans)
- Availability: Low (If exploited, could severely deplete available lending pools)
## Remediation
### Patches
- The fix implemented by the marginfi team addressed the logic flaw by ensuring that the `transfer_to_new_account` instruction enforces state safety, preventing migration while a flash loan is active (i.e., checking the `ACCOUNT_IN_FLASHLOAN` flag). *Specific version numbers for the patched state in `marginfi-v2` were not provided.*
### Workarounds
- Since the issue was based on a logic flaw in a new instruction, the primary workaround would be to disable or avoid using the `transfer_to_new_account` instruction until the logic was verified, or to ensure all flash loans are closed before performing account migration operations.
## Detection
- **Indicators of Compromise:** Transactions involving concurrent calls between `lending_account_start_flashloan`, `transfer_to_new_account`, and `lending_account_end_flashloan` on the same margin account within a single atomic block.
- **Detection Methods and Tools:** Analysis of on-chain transaction flow logs for unexpected state changes in `MarginfiAccount` or instances where the `ACCOUNT_IN_FLASHLOAN` flag is active during an account migration instruction.
## References
- No CVE ID assigned.
- Vulnerability details found in the article: `Threat Contained: marginfi Flash Loan Vulnerability` by Asymmetric Security Research. (Date: Sep 17, 2025 – *Note: The date in the source article appears to be future-dated*).