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.