Vulnerability report - March 2023
On Monday March 21st, 2023, The Notional Team was alerted to a vulnerability in Notional V2 that would allow a well-capitalized hacker to drain protocol funds. We patched the vulnerability immediately, and all funds are safe. The whitehat will receive 250k USDC + 250k NOTE as a bounty.
Patching this vulnerability involved short-term mitigation measures undertaken immediately following the receipt of the report that led to the disabling of some protocol functionality, and then a long-term fix that fully solved the issue and allowed the protocol to resume full operation on April 17th, 2023.
The vulnerability
The vulnerability exploits Notional V2’s integration with Compound V2 and has three parts:
- Compound V2 cTokens are not donation resistant. The cToken exchange rate reads the balance of the underlying token on the contract to determine what one cToken is worth. This means that someone can transfer funds to the contract and cause a discrete increase in the value of a cToken.
- Using obscure (now disabled) functionality on Notional, a user could get himself into a position where they held a cash balance that represented a number of cTokens which was greater than all the cTokens actually in existence.
- Once the user is in this position, they can transfer cash to Compound and inflate the value of a cToken. Because their Notional account has a cash balance which represents more cTokens than the actual total supply, the value of their Notional account would increase by more than the amount they transferred to Compound. This could allow them to inflate the value of their account to an arbitrarily high value and withdraw all of Notional’s funds. Their profit would roughly equal Notional’s TVL - the transfer to Compound.
Creating an inflated Notional cash balance
Notional’s cash balance system is an internal accounting mechanism that tracks account claims on Notional’s actual cToken balance. At any point in time, the sum of cash balances across all Notional accounts equals Notional’s actual cToken balance. If all cash balances are positive, then the attack isn’t possible. But if there are large negative cash balances, then it is possible to have an account with a positive cash balance that is greater than the total cToken supply.
The only way to achieve this is to have one account with a very large fCash debt position and a second account with a very large fCash lend position. When the fCash positions settle, the first account will have a large negative cash balance and the second account will have a large positive cash balance - potentially greater than the total supply of cTokens.
Details of an example attack
Here are details of how an attacker could create a massively leveraged fCash position by transferring fCash between two accounts, let the accounts settle into large positive and negative cash balances, and then use the positive cash balance account to drain Notional.
- Attacker uses two accounts, an exploiter account and a bad debt account.
- The attacker deposits some amount of ETH into the bad debt account.
- The bad debt account transfers a lot of fCash maturing in one day to the exploiter account, and the exploiter account transfers a lot of fCash maturing in two days back to the bad debt account. This leaves the bad debt account with a big fCash debt maturing in one day collateralized by a big fCash lend position maturing in two days. The exploiter account has the opposite position - a big fCash lend position in one day and a big fCash debt position in two days. The attacker can use this mechanism to get a very high amount of leverage because short-dated fCash is subject to a minimal collateral haircut.
- When the one-day fCash position settles, the bad debt account will have a very large negative cash balance collateralized by one-day fCash and the exploiter account will have a very large positive cash balance and an fCash debt maturing in one day.
- The attacker then increases the value of a cToken by making a large donation to Compound because cToken valuation looks at the actual balance of the underlying sitting on the contract.
- Once the cToken value has been artificially increased, the bad debt account is now very insolvent and the exploiter account is very in-the-money because the value of a cash balance has changed while the value of fCash has not. At this point the attacker can withdraw funds from Notional using the exploiter account while leaving the bad debt account with an insolvency.
Immediate mitigations
This vulnerability is enabled by the fact that accounts can generate extremely large leverage positions via transferring fCash. Our first action was to disable fCash transferability. Without the ability to transfer fCash, the only way to generate an fCash debt is to borrow from the liquidity pool as per usual.
Disabling fCash transferability ensures that only fCash assets that conform to Notional’s quarterly maturities can exist. But it doesn’t fully patch the vulnerability - an attacker could still use the liquidity pools as a pass-through to generate the required position, although it would be even more complex than the above attack.
Our second mitigation measure was to pause providing liquidity and temporarily pause the system over the quarterly roll on March 23rd. Because this attack required settling fCash into a cash balance, pausing the system over the roll allowed us to be sure that there were no accounts who would wind up with the necessary position post-settlement.
We also knew that after the March roll, there could be no other attack until the June quarterly roll because that was the next nearest date where you could generate fCash due to our disabling of fCash transfers. This allowed us time to test and implement the full fix.
The full fix
To fully fix this vulnerability, on April 17th, 2023 we removed Notional’s funds from Compound V2. Notional user funds will now be held in underlying, in a donation-resistant contract. This action removes the root cause of this issue and allows Notional to continue operating as normal.
The code to fix this issue can be seen in this pull request. The code was audited in a private Sherlock contest. The results of the audit will be released soon after judging is completed.
While this code was written to mitigate potential downstream impacts, there are some potential implications for integrating smart contracts:
- Integrating contracts who expect to deposit cTokens and have hardcoded addresses (rather than fetching via Notional view functions) will revert due to an incorrect transfer allowance.
- Integrating contracts who expect to receive cTokens and have hardcoded addresses (rather than fetching via Notional view functions) may incorrectly assume that no tokens were transferred.
- Wrapped fCash wrappers deployed prior to the migration will not be able to execute mintViaAsset because the transfer allowance set during initialization does not target the correct cToken address.
- Wrapped fCash wrappers deployed prior to the migration will send the proper nwToken balance back to the receiver during redeemViaAsset, however, this will not match the cToken address. If an integrating contract expects to receive cTokens, it may revert or incorrectly assume that it has not received any tokens.
As always, please reach out to us via Discord if you need assistance with smart contract integrations.
Conclusion
Removing the funds from Compound reduces the yield that LPs earn and the COMP incentives that the protocol accrues, but it is the right thing to do in the interests of safety and security.
We look forward to the launch of Notional V3 and putting that cash back to work in a different money market that is donation resistant and that we can be confident is secure. We continue to prioritize the safety of our user funds and will continue to test and audit our code, and actively monitor our bug bounty program through Immunefi.
The Notional Team
Notional Finance Newsletter
Join the newsletter to receive the latest updates in your inbox.