Abracadabra Rounding Error Attack
Rounding error in Abracadabra protocol allowed attacker to inflate base value without corresponding adjustment of elastic value
Description
The vulnerability stems from an intricate implementation of a RebaseToken mechanism in the Abracadabra protocol:
-
The protocol used a RebaseToken with two key components:
- Elastic value: Represents the actual assets in debt
- Base value: Represents total borrowed shares
-
The
User borrow mapping
only stored borrowed shares per user, not the actual assets -
The design appeared to intentionally allow the protocol (or anyone) to modify the assets owed by users by changing the elastic value of the RebaseToken
-
The
repayForAll()
function made this feature accessible to everyone
Attack Mechanism:
- A critical rounding error allowed the attacker to inflate the base value without correspondingly adjusting the elastic value
- This rounding error only manifests when elastic and base values significantly deviate from each other
- The
repayForAll()
function was instrumental in creating this deviation
Exploitation Steps:
- Inflate the base value (getting to elastic = 1, base = infinite)
- Repay the outstanding assets (elastic = 0)
- Leverage the first depositor problem, when elastic = 0, shares minted will be base = elastic
- Borrow all assets in the protocol, because of first depositor problem, shares minted will be 1:1.
- The attacker now has all assets in the protocol, but only owes a negligble share ratio to all outstanding shares.
Vulnerability Details:
- The solvency check
_isSolvent()
relied on comparing the attacker’s ratio of total borrowed shares - The solvency check implicitly assumed elastic and base values would remain closely aligned
- With base set to infinite, the attacker’s debt ratio becomes negligible
Proposed Solution
Assuming the original implementation was well-intentioned but flawed, a simple invariant check could have prevented the attack:
This assertion ensures that when no assets are owed (elastic = 0), there should be no debt shares (base = 0), thereby preventing the exploit.