Welkom terug bij Sherlock’s Vulnerability Spotlight, waar we een impactvolle kwetsbaarheid belichten die is ontdekt tijdens een Sherlock-audit. Deze week onderzoeken we een onjuiste berekening van het onderpandniveau die is gevonden in de @plaza_finance wedstrijd door @0xadrii, @KupiaSecurity, @f, @farman1094_, en @0xnovaman33.
Samenvatting van de kwetsbaarheid: In getRedeemAmount(...), berekent het contract het onderpandniveau voor BOND-inwisselingen met behulp van de post-trade status: // huidige code (kwetsbaar) onderpandNiveau = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Omdat de inwisselingsprijs vervolgens dit geschatte onderpandniveau gebruikt om te beslissen of de maximale prijs ($100) moet worden betaald, kan een aanvaller BOND kopen tegen gemengde (soms afgeprijsde) creatiekoersen totdat het (huidige) onderpandniveau ≤ 1.2 daalt, en vervolgens alle BOND inwisselen tegen de $100 cap—berekend met een op maat gemaakte depositAmount die het geschatte onderpandniveau weer boven 1.2 duwt. Dit stelt de aanvaller in staat om ETH te extraheren ("risicoloze" spread) totdat het huidige onderpandniveau van de pool de drempel bereikt.
Aanvalstappen: 1) Opzetfase Pool voorbeeldparameters: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, ETH prijs = $3075 Prijsregels: Creëer (koop) BOND: - Als collateralLevel ≤ 1.2: creationRate = tvl * 0.8 / bondSupply - Anders: creationRate = $100 Verlos (verkoop) BOND: - Als geschatte collateralLevel ≤ 1.2: redeemRate = tvl * 0.8 / bondSupply - Anders: redeemRate = $100
2) Fase A – Koop BOND tegen gemengde/kortingsprijzen - Koop BOND terwijl je het (huidige) collateralLevel monitort = tvl / (bondSupply * 100). - De eerste aankoop vindt plaats terwijl collateralLevel > 1.2 → mint nabij $100 per BOND. - Latere aankopen drukken het huidige collateralLevel onder 1.2 → mint tegen een kortingscreatieprijs (bijv. ~$94.07 in), waardoor je goedkoop een groot BOND-saldo opbouwt.
3) Fase B – Verlos alle BOND tegen de maximale prijs - Roep redeem(BOND, depositAmount = attackerBondBalance, ...) - Het contract berekent het geschatte onderpandniveau met behulp van de post-redeem status (bondSupply - depositAmount) en (tvl - depositAmount * 100) en (in het voorbeeld) krijgt een waarde > 1.2 - Omdat die geschatte waarde de drempel overschrijdt, wordt de redeemRate ingesteld op de maximale $100, waardoor de aanvaller in staat is om alle verzamelde BOND uit te cashen tegen $100
4) Winstrealisatie - Het verschil tussen gedisconteerde aankopen en $100 verkopen levert netto ETH-winst op. - In de PoC-cijfers: het uitgeven van 60 ETH over twee aankopen levert ~61,89 ETH op bij inlossing → ~1,89 ETH winst. - De aanvaller kan itereren totdat het huidige poolcollateraalniveau valt tot ~1,2, waarbij ongeveer wordt geëxtraheerd: extracteerbare USD ≈ ethPrice × poolReserve − 120 × bondSupply
Wat is de impact? Directe fondsextractie / waarde-lekkage: Aanvaller mint tegen verlaagde tarieven en lost in voor $100, waardoor ETH uit de pool wordt gehaald. Onbeperkt tot drempel: Kan doorgaan totdat het huidige onderpandniveau van de pool krimpt tot ≈ 1,2. Systemische prijsongelijkheid: Creëert arbitrage waarvoor eerlijke gebruikers betalen via slechtere poolreserves/resultaten.
De Oorzaak: Gebruik van post-trade status voor het prijzen van terugbetalingen De code berekent collateralLevel alsof de terugbetaling al heeft plaatsgevonden: // gebruikt (tvl - depositAmount*100) en (bondSupply - depositAmount) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Dit laat een aanvaller toe om depositAmount te kiezen zodat het geschatte niveau de drempel overschrijdt (> 1.2), waardoor de $100 redeemRate wordt ontgrendeld, zelfs wanneer de huidige poolstatus dit niet zou rechtvaardigen. 2. Drempelbeperking op een gemanipuleerde metriek De $100 cap beslissing hangt af van dit manipuleerbare geschatte collateral niveau in plaats van het huidige (pre-trade) niveau, wat prijsmanipulatie mogelijk maakt.
De Mitigatie: Bereken de terugkoopprijs op basis van de huidige poolstatus, niet de geschatte saldi na terugkoop. De voorgestelde oplossing van het project is correct: - collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + collateralLevel = (tvl * PRECISION) / (bondSupply * BOND_TARGET_PRICE);
Aanvullende Versterking (aanbevolen): 1. Prijsmonotonie: Zorg ervoor dat de inwisselingsprijs niet stijgt naarmate het depositAmount toeneemt (geen "meer verkopen, een betere eenheidsprijs krijgen"). 2. Invariant-gebaseerde prijsstelling: Afleiden van creëren/inwisselen vanuit een enkele invariant zodat de koop/verkoop-symmetrie eenzijdige arbitrage voorkomt. 3. Slippage-controles: Vereis door de gebruiker opgegeven min/max tarieven voor zowel creëren als inwisselen. 4. Limiet & throttling: Per transactie en per blok inwissellimieten om schade te beperken als drempels worden benaderd. 5. Consistentie over paden: Stem creëren en inwisselen paden af om dezelfde definitie van onderpandniveau te gebruiken (alleen huidige staat).
We zijn er trots op @plaza_finance te hebben geholpen met deze ontdekking. Wanneer het absoluut veilig moet zijn, is Sherlock de juiste keuze.
3,27K