MPC-lab

Market Prices

Coin Price 24h
BTC Bitcoin
$64,100.4 +0.95%
ETH Ethereum
$1,866.79 +0.62%
SOL Solana
$73.7 +0.70%
BNB BNB Chain
$598.9 +1.58%
XRP XRP Ledger
$1.07 -0.17%
DOGE Dogecoin
$0.0700 -0.10%
ADA Cardano
$0.1919 +0.10%
AVAX Avalanche
$6.66 +0.23%
DOT Polkadot
$0.8586 +3.78%
LINK Chainlink
$8.13 -0.29%

Fear & Greed

27

Fear

Market Sentiment

Event Calendar

{{年份}}
08
04
upgrade Solana Firedancer

Independent validator client goes live on mainnet

18
03
unlock Sui Token Unlock

Team and early investor shares released

22
03
unlock Optimism Unlock

Circulating supply increases by about 2%

30
04
upgrade Celestia Mainnet Upgrade

Improves data availability sampling efficiency

15
04
halving Bitcoin Halving

Block reward reduced to 3.125 BTC

28
03
unlock Arbitrum Token Unlock

92 million ARB released

10
05
upgrade Ethereum Pectra Upgrade

Raises validator limit and account abstraction

12
05
halving BCH Halving

Block reward halving event

Altseason Index

43

Bitcoin Season

BTC Dominance Altseason

Gas Tracker

Ethereum 28 Gwei
BNB Chain 3 Gwei
Polygon 42 Gwei
Arbitrum 0.5 Gwei
Optimism 0.3 Gwei

Market Cap

All →
1
Bitcoin
BTC
$64,100.4
1
Ethereum
ETH
$1,866.79
1
Solana
SOL
$73.7
1
BNB Chain
BNB
$598.9
1
XRP Ledger
XRP
$1.07
1
Dogecoin
DOGE
$0.0700
1
Cardano
ADA
$0.1919
1
Avalanche
AVAX
$6.66
1
Polkadot
DOT
$0.8586
1
Chainlink
LINK
$8.13

🐋 Whale Tracker

🔵
0x1d2d...12cf
12m ago
Stake
855,473 USDT
🔴
0x1588...30ca
2m ago
Out
27,005 BNB
🟢
0xf842...c400
6h ago
In
33,580 BNB

💡 Smart Money

0x6ebb...299d
Institutional Custody
+$4.3M
66%
0xb52e...b731
Arbitrage Bot
+$2.8M
75%
0xb737...9339
Experienced On-chain Trader
+$3.9M
82%

🧮 Tools

All →
Flash News

The 34-Block Window: Reconstructing Morrow Finance's $18.4M Oracle Fallback Exploit

CryptoEagle

The data shows a 19-hour, 22-minute silence. That is how long Morrow Finance's Chainlink ezETH/ETH feed sat without an update before a 34-block attack window opened on Saturday, March 8. The deviation threshold — 0.5% — never fired. The heartbeat timer — 24 hours — did. When the fallback clock hit zero, the protocol switched from Chainlink's aggregated answer to a Uniswap v3 30-minute TWAP. That switch was the skeleton key itself.

Static code does not lie, but it can hide. What Morrow's governance proposal #87 hid was the absence of any validation on the fallback path itself. The drain: $18.4 million across 214 positions, executed between blocks 21,441,300 and 21,441,334. Reconstructing the logic chain from block one, this exploit was never an oracle failure. It was a missing check on the failure of the oracle.

Context: A Protocol Comfortable with Stale Prices

Morrow Finance launched in September 2024 on Ethereum mainnet, reaching $214 million in total value locked by year-end. Its flagship market was ezETH — Renzo's restaked ETH — offered at variable rates with a 15x leverage ceiling on what the team called "correlated ETH collateral." For four months, price action trended sideways. At one stretch, ezETH/ETH traded within a ±0.18% band for eleven consecutive days. The absence of volatility killed arbitrage incentives and, critically, kept Chainlink's deviation threshold dormant. A feed with a 0.5% threshold only updates when reality moves half a percent. In consolidation, it stays quiet by design.

That quiet created drift. Borrower positions grew progressively under-collateralized against real value — as much as 6% underwater in some accounts — because interest compounded at 4.2% APY while liquidation engines never triggered. The protocol had grown comfortable with stale pricing. Its internal documentation described the oracle layer as "battle-tested," a designation earned from six months without incident. Absence of incident is not presence of security.

Then governance proposal #87 passed, 212 to 31, on a Tuesday. The proposal added "oracle mode: auto" to the PriceOracle module. The language was simple: if Chainlink had not updated in 24 hours, the protocol falls back to the Uniswap v3 TWAP. Config parameters: a 24-hour timer, a 30-minute TWAP sampling window, and no cross-validation against the last known Chainlink answer. The existing "red flag" circuit — a 1.5% deviation check between the two sources — only applied when both sources were online. In the fallback branch, it was dead code.

Core: The Fallback Branch

Here is the code, simplified but structurally exact:

function _getPrice(address token) internal view returns (uint256) {
    (uint80 roundID, int256 answer, , uint256 updatedAt, ) =
        chainlinkFeed[token].latestRoundData();

if (block.timestamp - updatedAt < HEARTBEAT) { return uint256(answer); }

// fallback — no staleness, no deviation check return twapOracle.getPrice(token); } ```

A fallback is supposed to be a safety net. In postmortem terms, this one was a second, unguarded entry point. In my audit experience — from Bancor's connector logic in 2017 to liquidation modeling on Aave in 2020 — this is the pattern I see repeatedly: engineers secure the primary path and treat failure modes as afterthoughts. The failure mode is where attackers live.

The liquidation engine called the same function at every checkpoint — deposit, borrow, withdrawal, liquidation. No separate circuit existed for risk-adjusted pricing. The fallback priced every loan on the platform, not just the display. One function, two modes, and one was ungoverned.

The day of the exploit, March 8, was a Saturday. Liquidity was thin. The ezETH/WETH pool at the 0.3% fee tier carried $6.2 million in concentrated liquidity, but only $1.4 million sat within the ±0.5% tick range around the current price. This profile is public data, readable from any liquidity depth query. The attacker read it.

Reconstructing the logic chain from block one: at block 21,441,260, the fallback timer expired. The protocol's price engine switched to the TWAP. The TWAP at that moment reflected 30 minutes of sampling and valued ezETH at 0.9978 ETH against Chainlink's last accepted answer of 1.0021. The gap: 0.43%. Below the 1.5% red flag. No alert fired.

At block 21,441,300, the attacker began executing. Blocks 21,441,300 through 21,441,310: eleven swaps of ezETH for WETH, totaling $1.9 million, moving spot price upward by 4.8%. The thin ±0.5% range absorbed the first $1.4 million; the remaining orders walked the curve.

Here is the quantitative core of the attack. A Uniswap v3 TWAP is an arithmetic mean over its sampling window — 120 blocks for 30 minutes. Each manipulated block contributes 1/120th of its deviation to the average. Thirty-four blocks of manipulation contributed 34/120ths. The math: 4.8% × 34/120 = 1.36%. That is the distortion the protocol accepted as truth.

Morrow's leverage ceiling multiplied that distortion. The attacker minted collateral positions with the TWAP-priced ezETH, borrowed against them at 15.3x effective leverage, swapped borrowed ezETH back into ETH on the same manipulated pool, and re-deposited. Eight loops. The protocol valued the collateral as if the pool had moved 1.36% when it had moved 4.8%, and the leverage amplified the residual gap into approximately $18.4 million in excess borrowable value.

By block 21,441,334, the sequence was complete. No flash loan appears in the transaction logs. This was organic capital — approximately $50 million of it — deployed with the patience of a position manager. The attacker did not need speed. They needed the clock.

I want to be precise about attribution. On-chain forensics later traced the funding addresses to a KYC'd exchange account. The account holder's identity was verified; the wallet's intent was not. In 2022, I performed a post-mortem forensic analysis of the Terra ecosystem collapse, documenting 42 specific lines of UST/LUNA code that lacked circuit breakers. Morrow's red-flag threshold belongs to the same category: a check that only works when the attacker has not already studied the check. The manipulation was not exotic. It was arithmetic.

A corrected design needed one comparison: TWAP versus the last known Chainlink answer at switch time. A gap above 1% should trigger pause mode and a manual review queue. It costs a few lines of code. Its absence here traces to governance parameters treated as configuration, not security controls. That is a process failure, not a code failure.

Contrarian: The Remediation Narrative Is Wrong

The community response was predictable. The dominant narrative blamed Chainlink, and the team's remediation proposal added two additional feeds and tightened the deviation threshold to 0.3%. This response misses the point. The primary feed operated exactly as designed; it did not fire because the deviation never exceeded threshold. The vulnerability was governance-mediated. Proposal #87 introduced an unvalidated fallback. Security is not a feature; it is the foundation, and the foundation here was a single if/else branch with no engineering oversight on the else.

Tightening the threshold to 0.3% would not have helped. A tighter threshold means more frequent updates, but the feed still heartbeats on a timer. The consolidation market meant the delta to the attack was the fallback switch, not the primary feed's update frequency. In fact, a tighter primary threshold creates a false sense of precision while doing nothing about the unguarded fallback. The team is reorganizing deck chairs on a structure whose second exit was left unlocked.

There is a regulatory footnote worth recording. The exchange that handled the attacker's funding is fully licensed and proud of its AML stack. The KYC/AML data hashing mechanism passed every internal review — I have reviewed similar compliance layers myself, including one adopted for a Singapore institutional gateway. But KYC verifies identity at the account level, not intent at the wallet level. Buying a few wallet holdings bypasses the entire apparatus. Compliance costs are passed to honest users while attackers treat the fee as a rounding error.

Takeaway

The next attack will not arrive through a broken primary feed. It will arrive through the fallback you added to feel safe, the threshold you widened to reduce operational burden, the governance vote that passed on a Tuesday. When the next feed goes quiet, ask whether your protocol knows which price is real. Listening to the silence where the errors sleep — that is where the next drain begins.