Anatomy of an oracle manipulation
Xora Labs Security · 10 min read
Almost every oracle exploit is the same four steps. The interesting question is not how the attack works, it is why the price was trusted in the first place.
The shape of the attack
Strip the specifics away and oracle manipulations follow one pattern. An attacker borrows a large amount of capital, uses it to move the price in whichever venue the protocol reads, calls the protocol while the price is wrong, and repays the loan in the same transaction. The profit is the difference between what the protocol thought the collateral was worth and what it was actually worth.
Flash loans did not create this class of bug. They removed the capital requirement, which turned an attack that needed a wealthy adversary into one that needs a competent one.
A spot price is not a price oracle
The most common root cause we see is a protocol reading the instantaneous reserve ratio of an AMM pool and treating it as a valuation. It is not a valuation. It is the marginal price at which the next unit would trade in one specific venue, and it can be moved by anyone willing to pay the slippage.
The cost of moving it is a function of pool depth. In a pool with ten million dollars of liquidity that cost is prohibitive. In a pool with eighty thousand dollars of liquidity, which is the pool a newly listed collateral asset actually trades in, it is not. Protocols are rarely exploited on their deepest market.
Time-weighting helps, and is not a solution
A time-weighted average raises the cost of manipulation because the attacker has to hold the price away from fair value for the length of the window rather than for one block. That is a real improvement and it is why TWAPs are the default recommendation.
It is not sufficient on its own, for three reasons worth checking in any review.
- Window length is a trade-off, not a setting. A short window is cheap to manipulate. A long window is slow to reflect a genuine repricing, which is its own liquidation risk during a real crash.
- Cost still scales with depth. A thirty minute TWAP on a shallow pool can still be worth attacking. The defence is depth, and the window only multiplies it.
- Cheap blockspace changes the maths. The cost of holding a price for N blocks is very different on a chain with low fees and fast blocks than on Ethereum mainnet.
What we check
When we review a protocol's pricing, these are the questions that find problems, in roughly the order they find them.
- Which exact venue is read, and what is the liquidity in that venue on a bad day rather than an average one?
- What happens when the feed is stale? A protocol that silently uses the last known price during an outage has built a delayed-manipulation primitive.
- Is there a deviation bound between sources, and what happens when it trips? Halting is a decision with its own risk, but an undefined answer is worse.
- Are borrow caps and collateral caps set relative to the depth of the market that prices the asset? This is usually where a shallow-pool exposure is actually contained.
- Who can add a new collateral asset, and does that process include a liquidity assessment or only a governance vote?
The uncomfortable part
Most oracle findings are not subtle bugs in the pricing code. The pricing code is usually fine. The finding is that a correct implementation was pointed at a market that was too thin to support the value the protocol allowed against it, and nobody re-checked when the market changed.
That makes this a monitoring problem as much as an audit problem. Liquidity moves after launch, and an assumption that held at deployment does not necessarily hold a year later.
Working on something in this area? Tell us about it.