Disclosure: Fixing a critical bug in the Sense Space Oracle

Joshua Levine
Sense Finance
3 min readJun 9, 2022

--

On Friday, April 22nd, Sense received a critical bug disclosure from Violet Vienhage regarding the Space AMM oracle. We immediately paused the Sense Fuse Pool, the main consumer of the oracle, and followed up with an announcement of the bug on our discord.

No user funds were lost, are currently at risk, or were ever at risk.

Users can continue to safely earn fixed rates, go long/short future yields, and provide liquidity on Sense Space. Borrowing & Lending on the Sense Fuse Pool has been halted until further notice.

Sense Finance has paid the full $50,000 bounty listed on our ImmuneFi Bug Bounty Program and has already deployed a fix to mainnet.

Below, we describe the bug and our resolution.

The bug

A user could manipulate the oracle data at the most recent timestamp for a given Space pool by sending dummy swaps, incurring only the gas costs associated with the transaction. An attacker could do this every x minutes and drive the TWAP of the Space pool in whichever direction they’d like.

Context

Prior to adding the oracle to our Balancer Pool, the onSwap (Space.sol#L325) the function did not mutate any storage slots. As a result, the onlyVault (Space.sol#L807) modifier we use for the joins and exits — which makes it so that only the Balancer Vault can call those functions — was not used for our swap function. This was a deliberate omission since we were using onSwap as a preview function elsewhere in our codebase. ie, we were using it to check how much out we’d get for a given amount in, or vice versa, before actually executing the swap.

Unfortunately, we didn’t catch that when we added the oracle component later in our dev cycle for v1, there was now a storage slot mutation that shouldn’t have been accessible without actually performing a swap. Namely, updating the Space oracle price buffer should only happen when there’s a valid swap involving transferred tokens, but because an actual swap only actually takes place if onSwap is called through the Balancer V2 Vault, one could corrupt the oracle date by calling it directly as a “preview” function with garbage swap data.

What we did

Upon receiving word of the bug, we wrote tests validating the effects and considered its impact. Because we hadn’t publicized our Fuse pool yet, there were no assets at risk and it was an easy decision to preemptively pause the pool. We did that immediately, confirmed the severity with the reporter, paid the bounty, then began work on a fix.

The fix was simple and minimal — we added a check to the oracle update path in onSwap such that it was only triggered when called by the Vault (Space.sol#L339). That way, we can still use the function as a preview, but it became functionally a view except when called by the Vault (checked with this test). We deployed the updated Space contract to production right before we initialized the stETH Series with maturities in 3mo, 6mo, 1 year, and 5 years (our “web3 yield curve”), on May 19th, 2022 at 12:50 UTC.

Final thoughts

Security is our highest priority at Sense. The Sense Protocol has undergone various forms of quality assurance, such as unit/integration/manual testing, fuzz testing, and multiple 3rd party audits. In addition, after our v1 launch in general we have employed a few new internal dev processes (inspired by Nascent’s simple security toolkit) to increase our own confidence in the features we release. To catch post-launch bugs, we will continue to have a public bug bounty on ImmuneFi, and we have plans to employ Formal Verification in future versions of Sense.

We’d like to thank Violet once again for finding this bug and working with us to understand its impact. If you’re a whitehat, we’d love for you to check out our ImmuneFi Bug Bounty Program and help secure the Sense Protocol.

--

--