Välkommen tillbaka till Sherlocks Vulnerability Spotlight, där vi lyfter fram en effektfull sårbarhet som upptäcktes under en Sherlock-granskning. Den här veckan undersöker vi en felaktig beräkning av säkerhetsnivån som hittades i @plaza_finance-tävlingen av @0xadrii, @KupiaSecurity, @f, @farman1094_ och @0xnovaman33.
Sammanfattning av sårbarheten: I getRedeemAmount(...) beräknar kontraktet säkerhetsnivån för inlösen av obligationer med hjälp av tillstånd efter handel: Aktuell kod (sårbar) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Eftersom inlösenprissättningen sedan använder denna uppskattade säkerhetsnivå för att avgöra om maxpriset (100 USD) ska betalas, kan en angripare köpa BOND till blandade (ibland rabatterade) skapandekurser tills den (aktuella) säkerhetsnivån sjunker ≤ 1,2 och sedan lösa in all BOND till taket på 100 USD – vilket beräknas med ett utformat depositAmount som pressar tillbaka den uppskattade säkerhetsnivån över 1,2. Detta gör det möjligt för angriparen att extrahera ETH ("riskfri" spread) tills poolens nuvarande säkerhetsnivå når tröskeln.
Attacksteg: 1) Installationsfas Parametrar för poolexempel: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, ETH pris = $3075 Regler för prissättning: Skapa (köp) BOND: - Om säkerhetsnivå ≤ 1.2: creationRate = tvl * 0.8 / bondSupply - Annars: creationRate = $100 Lös in (sälj) BOND: - Om uppskattad säkerhetNivå ≤ 1,2: redeemRate = tvl * 0,8 / bondSupply - Annars: redeemRate = $100
2) Fas A – Köp BOND till blandade/rabatterade priser - Köp BOND samtidigt som du övervakar (aktuell) collateralLevel = tvl / (bondSupply * 100). - Första köpet sker medan säkerhetsnivån > 1,2 → präglar nära 100 dollar per OBLIGATION. - Efterföljande köp pressar den nuvarande säkerhetsnivån under 1,2 → mynta till rabatterad skapandehastighet (t.ex. ~94,07 dollar), vilket ackumulerar ett stort BOND-saldo billigt.
3) Fas B – Lös in alla BOND till maxpris - Samtalsinlösen (BOND, depositAmount = angripareBondBalance, ...) - Kontraktet beräknar uppskattad säkerhetsnivå med hjälp av tillstånd efter inlösen (bondSupply - depositAmount) och (tvl - depositAmount * 100) och får (i exemplet) ett värde > 1,2 - Eftersom det uppskattade värdet överstiger tröskelvärdet är redeemRate satt till max 100 USD, vilket gör det möjligt för angriparen att ta ut all ackumulerad BOND på 100 USD
4) Realisering av vinst - Skillnaden mellan rabatterade köp och försäljningar på 100 dollar ger en nettovinst på ETH. - I PoC-siffrorna: att spendera 60 ETH över två köp ger ~61,89 ETH vid inlösen → ~1,89 ETH vinst. - Angriparen kan iterera tills den aktuella säkerhetsnivån för poolen sjunker till ~1,2 och extrahera ungefär: extraherbara USD ≈ ethPrice × poolReserve − 120 × bondSupply
Vad är effekten? Direkt utvinning av medel / värdeläckage: Angriparen myntar till rabatterade priser och löser in till $100, vilket suger ETH från poolen. Obunden till tröskelvärdet: Kan fortsätta tills poolens aktuella säkerhetsnivå krymper till ≈ 1.2. Inkonsekvens i systemisk prissättning: Skapar arbitrage som ärliga användare betalar för via sämre poolreserver/resultat.
Grundorsaken: Användning av efterhandelstillstånd för inlösen av prissättningsförfaranden Koden beräknar collateralLevel som om inlösen redan har skett: användningsområden (tvl - depositAmount*100) och (bondSupply - depositAmount) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Detta gör att en angripare kan välja depositAmount så att den uppskattade nivån passerar tröskeln (> 1,2), vilket låser upp redeemRate på 100 $, även när det aktuella pooltillståndet inte skulle motivera det. 2. Tröskelvärde för grind på ett manipulerat mått Beslutet om ett tak på 100 dollar beror på denna manipulerbara uppskattade säkerhetsnivå snarare än den nuvarande nivån (före handel), vilket möjliggör prisspel.
Åtgärden: Beräkna inlösningspriser från det aktuella pooltillståndet, inte de uppskattade saldona efter inlösen. Projektets föreslagna korrigering är korrekt: - collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * PRECISION) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + collateralLevel = (tvl * PRECISION) / (bondSupply * BOND_TARGET_PRICE);
Ytterligare härdning (rekommenderas): 1. Prismonotonicitet: Se till att inlösenpriset inte ökar när depositAmount ökar (inget "sälj mer, få ett bättre enhetspris"). 2. Invariantbaserad prissättning: Härled skapa/lös in från en enda invariant så att köp/sälj-symmetri förhindrar ensidigt arbitrage. 3. Glidningskontroller: Kräv minimi-/maxpriser som tillhandahålls av användaren för både skapande och inlösen. 4. Cap & throttle: Tak per tx och per block för att begränsa skadan om trösklar närmar sig. 5. Konsekvens mellan sökvägar: Justera, skapa och lös in sökvägar för att använda samma definition av säkerhetsnivå (endast aktuellt tillstånd).
Vi är stolta över att ha hjälpt till att säkra @plaza_finance genom denna upptäckt. När det absolut måste vara säkert är Sherlock det rätta valet.
3,27K