Tervetuloa takaisin Sherlockin haavoittuvuuksien valokeilaan, jossa nostamme esiin Sherlock-tarkastuksessa paljastuneen vaikuttavan haavoittuvuuden. Tällä viikolla tarkastelemme virheellistä vakuustason laskelmaa, joka löytyi @plaza_finance kilpailusta @0xadrii, @KupiaSecurity, @f, @farman1094_ ja @0xnovaman33.
Yhteenveto haavoittuvuudesta: Sopimuksessa getRedeemAmount(...) sopimus laskee BOND-lunastusten vakuustason käyttämällä kaupan jälkeistä tilaa: Nykyinen koodi (haavoittuvainen) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * TARKKUUS) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); Koska lunastushinnoittelu käyttää tätä arvioitua collateralLevel-tasoa päättääkseen, maksaako se maksimihinnan (100 dollaria), hyökkääjä voi ostaa BONDia sekamääräisillä (joskus diskontatuilla) luontikursseilla, kunnes (nykyinen) vakuustaso laskee 1,2:≤, ja lunastaa sitten kaikki BOND-arvot 100 dollarin ylärajalla – laskemalla muotoillulla talletuksellaAmount, joka nostaa arvioidun vakuustason takaisin yli 1,2:n. Näin hyökkääjä voi poimia ETH:ta ("riskitön" spread), kunnes poolin nykyinen vakuustaso saavuttaa kynnyksen.
Hyökkäyksen vaiheet: 1) Asennusvaihe Poolin esimerkkiparametrit: - poolReserve = 120 ETH, bondSupply = 3000, levSupply = 200, ETH price = 3075 dollaria Hinnoittelua koskevat säännöt: Luo (osta) BOND: - Jos vakuusTaso ≤ 1.2: luominenRate = tvl * 0.8 / joukkovelkakirjaTarjonta - Else: creationRate = 100 dollaria Lunasta (myy) BOND: - Jos arvioitu vakuusTaso ≤ 1,2: lunastuskorko = tvl * 0,8 / joukkovelkakirjalainaTarjonta - Else: redeemRate = 100 dollaria
2) Vaihe A – Osta BONDia seka-/alennetuilla hinnoilla - Osta BOND samalla kun seuraat (nykyistä) vakuuttaTaso = tvl / (bondSupply * 100). - Ensimmäinen osto tapahtuu, kun vakuustaso > 1,2 → lyödä lähes 100 dollaria BONDia kohden. - Myöhemmät ostot painavat nykyisen vakuustason alle 1,2 → alennetulla luomisnopeudella (esim. ~94,07 dollaria), jolloin suuri joukkovelkakirjasaldo kertyy halvalla.
3) Vaihe B – Lunasta kaikki BONDit maksimihintaan - Soita lunastus(BOND, depositAmount = hyökkääjäBondBalance, ...) - Sopimus laskee arvioidun vakuustason käyttämällä lunastuksen jälkeistä tilaa (bondSupply - depositAmount) ja (tvl - depositAmount * 100) ja saa (esimerkissä) arvoksi > 1,2 - Koska arvioitu arvo ylittää kynnysarvon, redeemRate on asetettu enintään 100 dollariin, jolloin hyökkääjä voi lunastaa kaiken kertyneen BONDin 100 dollarilla
4) Voiton realisointi - Alennettujen ostojen ja 100 dollarin myyntien välinen ero tuottaa netto-ETH-voiton. - PoC-luvuissa: 60 ETH:n käyttäminen kahteen ostoon tuottaa ~61,89 ETH:ta lunastuksesta → ~1,89 ETH:n voittoa. - Hyökkääjä voi iteroida, kunnes nykyinen poolin vakuustaso laskee ~1,2:een, poimimalla karkeasti: poimittavissa oleva USD ≈ ethPrice × poolReserve − 120 × bondSupply
Mikä on vaikutus? Suora varojen poiminta / arvovuoto: Hyökkääjä lyö alennettuun hintaan ja lunastaa 100 dollarilla, mikä sifonoi ETH:ta poolista. Rajoittamaton, kunnes kynnysarvo: Voi jatkua, kunnes poolin nykyinen vakuustaso kutistuu ≈ 1,2:een. Systeeminen hinnoittelun epäjohdonmukaisuus: Luo arbitraasia, josta rehelliset käyttäjät maksavat huonompien poolireservien/tulosten kautta.
Perimmäinen syy: Kaupan jälkeisen tilan käyttö lunastusten hinnoittelussa Koodi laskee collateralLevelin ikään kuin lunastus olisi jo tapahtunut: käyttää (tvl - depositAmount*100) ja (bondSupply - depositAmount) collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * TARKKUUS) / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); 1. Tämän avulla hyökkääjä voi valita depositAmount-arvon siten, että arvioitu taso ylittää kynnysarvon (> 1,2), jolloin 100 dollarin lunastusarvo avautuu, vaikka nykyinen poolin tila ei oikeuttaisi sitä. 2. Kynnysportti manipuloidussa mittarissa 100 dollarin ylärajapäätös riippuu tästä manipuloitavasta arvioidusta vakuustasosta nykyisen (kauppaa edeltävän) tason sijaan, mikä mahdollistaa hintapelaamisen.
Lievennys: Laske lunastuksen hinnoittelu poolin nykyisestä tilasta, älä arvioiduista lunastuksen jälkeisistä saldoista. Projektin ehdottama korjaus on oikea: - collateralLevel = ((tvl - (depositAmount * BOND_TARGET_PRICE)) * TARKKUUS) - / ((bondSupply - depositAmount) * BOND_TARGET_PRICE); + vakuustaso = (tvl * TARKKUUS) / (joukkovelkakirjalainatarjonta * BOND_TARGET_PRICE);
Lisäkarkaisu (suositus): 1. Hinnan yksitoikkoisuus: Varmista, että lunastushinta ei nouse depositAmountin kasvaessa (ei "myy enemmän, saat paremman yksikköhinnan"). 2. Invarianttipohjainen hinnoittelu: Johda luonti/lunastus yhdestä invariantista, jotta osto-/myyntisymmetria estää yksipuolisen arbitraasin. 3. Liukumisen tarkistukset: Vaadi käyttäjän antamat vähimmäis- ja maksimihinnat sekä luontiin että lunastukseen. 4. Cap & throttle: Tx- ja lohkokohtaiset lunastusrajat rajoittavat vahinkoa, jos kynnyksiä lähestytään. 5. Johdonmukaisuus polkujen välillä: Kohdista luonti- ja lunastuspolut käyttämään samaa vakuustason määritystä (vain nykyinen tila).
Olemme ylpeitä siitä, että olemme auttaneet turvaamaan @plaza_finance tämän löydön kautta. Kun sen on ehdottomasti oltava turvallinen, Sherlock on oikea valinta.
3,26K