Full Report
RAI (Reflexer Finance) is an ETH-backed stable asset with a managed float regime. DAI pegs to $1 through governance-controlled mechanisms, RAI only uses ETH as collateral. With this, it contains an algorithmic controller to manage the redemption price. On top of this, it's a lending protocol. When taking out a loan, the collaterialization ratio must not go below a certain threshold. If this is the case, then the loan is liquidated. This is an important aspect of keeping the system solvent and safe. RAI introduced a feature called Safe Saviours. These are contracts that attempt to rescue an underwater position during liquidation to inject additional collateral. The contracts are written by the community, as long as they meet certain requirements, but must be approved by Governance. The liquidation engine calls saveSAFE() on the Safe Saviours contract on liquidation. If anything goes wrong then the error is caught via a try/catch block and the liquidation happens anyway. It's important that loans are always liquidatable. Otherwise, the protocol would be left with a lot of bad debt and lose money. The error handling has two peculiar things. First, the amount of gas is NOT passed to the contract. By default, this means that 63/64 of the remaining gas is sent in order to allow the contract to finish execution even if callee contract uses all the gas. Second, the catch clause will emit an event with the revert reason without a limit on the amount of data. Returning data and event emissions both use gas. The issue is that an attacker can return a large amount of data from the contract call and force an out of gas error to occur. In practice, this violates a key invariant of lending protocols that all loans must be liquidatable. The bad debt would accumulate over time and would be permanent in the protocol. When this was reported, RAI claimed the vulnerability was out of scope because it required the contract to be whitelisted by Governance. Although this is true, there is a process for this to happen that is explicitly documented and encouraged. Hence, I effectively see this as unprivileged. Immunefi initially ruled this as a medium citing it as gas griefing. After being closed again by RAI, Immunefi closed as None impact. From the arguments I see from the article, I tend to side the researcher. For all intents and purposes, this functionality was exploitable and effectively unprivilged through normal flow. It led to protocol unsolevnecy and the ability to make bets to guarantee profit. To find the vulnerability, they looked for dangerous external calls. Additionally, they noted that try/catch is commonly mishandled in Solidity because it gives a false sense of security on error handling. According to the author, developers should use ExcessivelySafeCall for arbitrary untrusted calls to limit return data, cap gas on calls to external contracts and treat error messages as untrusted input. From the reporting side, they have some good points. First, Immunefi mediation is non binding, as previous rulings can be changed. Second, Governance rubber stamps are not a security boundary. If the approval process can't detect the bug, then it's not a mitigation. Overall, a pretty good post on calldata bombing.
Analysis Summary
# Vulnerability: Returndata Bombing in RAI Liquidation Engine
## CVE Details
- **CVE ID:** Not Assigned (Identified via Immunefi Bug Bounty)
- **CVSS Score:** N/A (Researcher argues Critical; Immunefi ruled None/Medium)
- **CWE:** CWE-770: Allocation of Resources Without Limits or Throttling (Gas Griefing/DoS)
## Affected Systems
- **Products:** Reflexer Finance (RAI)
- **Versions:** Affected codebase within the `geb` repository (specifically `LiquidationEngine.sol`).
- **Configurations:** Systems utilizing the "Safe Saviours" feature where community-developed contracts are whitelisted by governance.
## Vulnerability Description
The vulnerability is a **Returndata Bomb** attack targeting the `liquidateSAFE()` function. RAI allows "Safe Saviour" contracts to intervene during liquidations to inject collateral. The protocol uses a `try/catch` block to handle calls to these external contracts to ensure liquidations proceed even if a savior fails.
However, two technical flaws exist:
1. **Uncapped Gas:** The call to `saveSAFE()` follows the EIP-150 "63/64 rule," passing most available gas to the external contract.
2. **Unbounded Returndata Copying:** The `catch (bytes memory revertReason)` clause implicitly uses the `RETURNDATACOPY` opcode.
An attacker can deploy a malicious savior contract that reverts with a massive amount of data. When the `LiquidationEngine` attempts to copy this oversized error message into memory within the `catch` block, it consumes the remaining 1/64 of available gas, triggering an **Out of Gas (OOG)** error. This causes the entire liquidation transaction to revert, making the "underwater" position unliquidatable.
## Exploitation
- **Status:** PoC available; identified by researchers (TrustSec).
- **Complexity:** Medium (Requires navigating the governance whitelisting process).
- **Attack Vector:** Network (Smart Contract Interaction).
## Impact
- **Confidentiality:** None
- **Integrity:** Medium (Protocol state becomes insolvent as bad debt accumulates).
- **Availability:** High (Liquidation functionality — a core protocol invariant — is disabled for specific positions).
## Remediation
### Patches
- No official patch was merged into the `geb` repository at the time of the report. The vulnerability is reportedly "silently patched" via governance (by not whitelisting malicious contracts).
### Workarounds
- **Governance Diligence:** Extremely strict auditing of Savior contracts before whitelisting.
- **Deregistration:** Manually removing malicious saviors via governance vote (though bad debt may accumulate during the voting period).
## Detection
- **Indicators of Compromise:** Failed liquidation transactions (reverting with Out of Gas) targeting specific SAFE positions.
- **Detection Methods:**
- Static analysis for `try/catch` blocks that catch `bytes memory` without size limits.
- Monitoring for savior contracts that utilize `revert` with large data offsets/lengths.
- Use of the `ExcessivelySafeCall` library to limit return data size.
## References
- Reflexer Finance: [https://reflexer.finance/](https://reflexer.finance/)
- Original Writeup: [https://www.trust-security.xyz/post/returndata-bombing-rai-s-liquidation-engine](https://www.trust-security.xyz/post/returndata-bombing-rai-s-liquidation-engine)
- Vulnerable Code: [https://github.com/reflexer-labs/geb](https://github.com/reflexer-labs/geb)