Full Report
Mirror Protocol allows users to take long or short positions on tech stocks. To bet on a stock, you must lock collateral for a minimum of 14 days. After the trade concludes, they can unlock the collateral to release the funds back to their wallet. What keeps track of this betting? A simple contract generated ID number that is returned to the user. The code takes in a list of IDs to use when withdrawing the collateral. The Mirror Protocol does not check the duplication of a position ID within this list. As a result, an attacker can unlock the collateral of a position over and over again. In the real attack, the position 43186 was duplicated 437 times. I would bet there was code to remove the usage of a particular ID from being withdrawn after this. However, the issue appears to be in the passing of the list. To fix the bug, a for loop checks to ensure that no duplicate IDs are included to be unlocked. Interesting! Even code written in Rust can have security issues.
Analysis Summary
# Vulnerability: Mirror Protocol Duplicate Position ID Collateral Unlock
## CVE Details
- **CVE ID:** Not assigned (Common in DeFi smart contract exploits)
- **CVSS Score:** 9.1 (Estimated) (Critical)
- **CWE:** CWE-694: Use of Multiple Proxies, or more specifically, a failure to handle duplicate inputs in a list processing logic.
## Affected Systems
- **Products:** Mirror Protocol (DeFi platform on Terra Classic)
- **Versions:** Smart contracts deployed prior to the May 2022 patch.
- **Configurations:** The `lock` and `mint` contracts responsible for handling collateral withdrawal and position management.
## Vulnerability Description
The flaw exists in the mechanism used to unlock collateral after a trade concludes. To withdraw funds, a user provides a list (vector) of Position IDs to the contract. The Rust-based smart contract failed to validate whether the same Position ID appeared multiple times within that list.
Because the contract processed each ID in the list sequentially without checking for duplication, it would trigger the "unlock" action for the same position multiple times. By intentionally inflating the `locked_amount` of a single position (e.g., by sending additional USTC directly to the contract) and then submitting a list containing that Position ID hundreds of times, an attacker can withdraw the collateral balance repeatedly, effectively draining the contract's liquidity.
## Exploitation
- **Status:** Exploited in the wild (Resulted in approximately $90M loss).
- **Complexity:** Low
- **Attack Vector:** Network (Smart Contract Interaction)
## Impact
- **Confidentiality:** None
- **Integrity:** High (Unauthorized manipulation of contract state and fund balances)
- **Availability:** High (Depletion of protocol treasury/liquidity)
## Remediation
### Patches
- The vulnerability was addressed in [commit 56cc694](https://github[.]com/Mirror-Protocol/mirror-contracts/commit/56cc6946b9457293ede6aa0feb296ee1d16f6974).
- The fix introduces a check within the loop to ensure that no duplicate IDs are processed in the `unlockable_positions` vector.
### Workarounds
- No viable workarounds for end-users; the fix required a smart contract migration or update by the protocol developers.
## Detection
- **Indicators of Compromise:** Transactions calling the unlock functions with excessively large arrays of identical Position IDs; discrepancy between `locked_funds` and actual contract balances.
- **Detection Methods:** On-chain monitoring for duplicate inputs in `MsgExecuteContract` calls; comparing total withdrawn amounts against the original position collateral.
## References
- Original Analysis by BlockSec: hxxps://blocksecteam[.]medium[.]com/how-the-mirror-protocol-got-exploited-33b5c1d48322
- Terra Finder Transaction: hxxps://finder[.]terra[.]money/classic/tx/08DD2B70F6C2335D966342C20C1E495FD7A8872310B80BAF3450B942F79EBC1F
- News Report: hxxps://www[.]theblockcrypto[.]com/post/149342/a-90-million-defi-exploit-on-terra-went-unnoticed-for-seven-months