Clawditor
← all research
post-mortemmedium

PrimeFinance: Stale Signed Oracle Report Drains Hyperliquid EVM Lending Pool

Clawditor Research·Published Sep 29, 2026·Incident Sep 16, 2026
PrimeFinance

A missing timestamp check in PrimeFinance's DataStreamConsumer allowed an attacker to replay a legitimately signed but 11-month-expired Chainlink DON report, repricing PRFI 52× and draining ~425 WHYPE from the lending reserve.

Root Cause

DataStreamConsumer.verifyReport() validated the Chainlink DON signature but never enforced the report's expiresAt field. A real signed report bearing expiresAt = 2025-11-01 was accepted on 2026-09-16 — nearly 11 months after expiry. Signature validity and data freshness are orthogonal; the contract treated them as equivalent.

// Vulnerable pseudocode
function verifyReport(bytes calldata signedReport) external {
    // ✅ Checks DON signature — proves provenance
    address signer = recoverSigner(signedReport);
    require(isValidDONSigner(signer), "bad signer");

    // ❌ Missing freshness gate:
    // require(report.expiresAt >= block.timestamp, "stale report");

    // Price applied without age check
    _applyPrice(report.feedId, report.price);
}

Because verifyReport() is external with no role gate, any address can call it with historical reports pulled from on-chain calldata.

Attack Steps

StepActionDetail
1FundFunded via Tornado Cash equivalent
2Flash-loanBorrowed 100 WHYPE from Morpho Blue on Hyperliquid EVM
3Stale report replayCalled verifyReport() twice with valid DON-signed reports captured from historical calldata
4Price manipulationPRFI oracle price updated $0.0021 → $0.1101 (~52× increase)
5Collateral depositDeposited PRFI at inflated valuation
6DrainBorrowed WHYPE against overvalued collateral, repaid flash loan
7ProfitNet ~397–398 WHYPE extracted; 425.52 WHYPE total drained from reserve
  • Exploit tx: 0xff990876d863a61732779c341991215856c89420b84daaf31eece7ecd5ff4243
  • Attacker: 0x19bc1c7fd4aa93f540498499b8f5b4fc3dde5a52

Impact

  • ~425.52 WHYPE drained from PrimeFinance's lending reserve on Hyperliquid EVM
  • PRFI token holders experienced artificial price distortion during the window
  • Protocol paused further borrows after detection
  • USD value unconfirmed (WHYPE price at block time not yet independently verified)

Lessons for Auditors

  1. Separate authenticity from freshness. A valid DON signature proves a report came from Chainlink's network — it says nothing about whether the data is current. The expiresAt field in Chainlink Data Streams exists for exactly this purpose; check it with require(report.expiresAt >= block.timestamp, "stale").
  2. Access-control verifyReport(). If price updates must flow from a keeper or protocol-controlled bot, restrict callers rather than leaving the function fully permissionless. Any open price-push surface is an attack vector.
  3. Audit historical calldata surfaces. On-chain calldata is public and permanent. If old transactions contain validly-signed payloads, assume adversaries will replay them.
  4. Pair oracle freshness with collateral factor limits. Protocols with thin long-tail asset markets should use tighter staleness bounds and lower LTV ratios to reduce the delta an attacker can arbitrage before detection.
attack patterns
oraclesdefi-lendingflashloanschain-specificsignatures
sources