15 Eylül 2023'te Güncellendi: Balancer, bu olayın tüm hikayesini, edinilen deneyimler ve çıkarılan dersler dahil olmak üzere ayrıntılı biçimde açıklayan resmi olay sonrası incelemeyi yayımladı. Karmaşık ve mükemmel anlatımıyla bu olay sonrası inceleme oldukça etkileyici olup kesinlikle okunmaya değer.
Güvenlik perspektifinden bakıldığında, bu olay sonrası inceleme iki hatanın varlığını ortaya koymaktadır. Birincisi, raporumuzda ele aldığımız aşağı yuvarlama hatasıdır; ikincisi ise raporumuzda 3.6 ve 3.7. saldırı adımlarında açıklanan "0 arzda oranı sıfırlama" sorunudur. Balancer'ın raporu ikincisini en kritik sorun olarak değerlendirirken birincisini katkıda bulunan bir etken olarak nitelendirmektedir. Ancak biz her iki hatanın da kârlı bir sömürü için eşit derecede önemli olduğunu düşünmekteyiz:
Birinci hata, token oranını şişirmek için kullanılır ve kârın temel nedeni olarak hizmet eder. Bu olmadan kâr elde etmek mümkün olmayacaktır.
İkinci hata, bb-a-token'larının borcunu dengeleyerek saldırıyı mümkün kılar. Bu olmadan, saldırı bb-a-token'larının düşük likiditesi nedeniyle başarısız olacaktır; zira bu token'ları elde etmek için başka bir kaynak bulunmamaktadır (saldırganın bunları bir şekilde temin etmesi hali hariç).
23 Ağustos 2023'te Balancer, birden fazla boosted pool'u etkileyen kritik bir güvenlik açığının varlığını kamuoyuyla duyurdu ve kullanıcıları etkilenen pool'lardan LP'lerini derhal çekmeye davet etti. Balancer, TVL'nin büyük bölümünü güvence altına almak için acil durum azaltma prosedürlerini başlatmış olsa da bazı fonlar risk altında kalmaya devam etti. Ne yazık ki 27 Ağustos'ta, beş gün sonra, gerçek dünyada birkaç saldırı gerçekleştiğini fark ettik. O tarihten bu yana 2,12 milyon doları aşan varlık çalındı.
Bu raporu kaleme aldığımız sırada (duyurudan üç haftadan fazla zaman geçmişken ve artık güvenli olduğuna inandığımız bir noktada) Balancer, bu güvenlik açığına ilişkin herhangi bir derinlemesine analiz yayımlamamıştır. Bu raporda, ağırlıklı olarak saldırı işlemlerinden birine dayanan kapsamlı bir analiz sunmayı amaçlıyoruz.
Temel Çıkarımlar (TL;DR)
- Araştırmamız, temel nedenin
linearpool'daki aşağı yuvarlama mantığından kaynaklanan fiyat manipülasyonundan ileri geldiğini ortaya koymaktadır. Bu durum, ilgiliboostedpool tarafından kullanılan önbelleğe alınmış token oranını olumsuz biçimde etkilemektedir. - Bu olay, güvenlik açığı içeren bir kaynaktan fork yapan projelerin zamanında bilgilendirilmesinin kritik önemini vurgulamaktadır; bu durum, tüm topluluk için gerçek anlamda önemli bir zorluk teşkil etmektedir.
- Süregelen çok sayıdaki saldırı, proaktif tehdit önlemenin zorunluluğunu gözler önüne sermekte ve bu önlemin olası kayıpların azaltılmasına kaçınılmaz biçimde katkı sağlayacağını ortaya koymaktadır.
Aşağıdaki bölümlerde önce Balancer hakkında bazı temel arka plan bilgileri sunacağız. Ardından güvenlik açığı ve bununla ilişkili saldırının kapsamlı bir analizini gerçekleştireceğiz. Son olarak, bugüne kadar gözlemlediğimiz saldırıların kısa bir özetini ve ilgili kârları sunacağız.
0x1 Balancer Hakkında Arka Plan Bilgisi
Balancer V2 [1], programlanabilir likidite için esnek bir yapı taşını temsil eden merkezi olmayan bir otomatik piyasa yapıcı (AMM) protokolüdür. Token muhasebesinin havuz mantığıyla eşleştirildiği diğer AMM'lerin aksine Balancer, token muhasebesi ve yönetimini havuz mantığından ayırır; bu sayede çok sayıda token transferini azaltarak takas verimliliğini artırabilir.
Balancer çeşitli havuz türlerini desteklemektedir. Her havuz, BPT (yani Balancer Pool Token) adlı bir LP token ile ilişkilidir. Temel olarak BPT değeri, tüm temel token'ların toplam değerine göre hesaplanır.
Balancer, Vault'a kayıtlı tüm havuzlardan en iyi fiyatlardan yararlanan batch swap olarak da bilinen çok adımlı takasları desteklemektedir. Özellikle Vault, çok adımlı takasları kolaylaştırmak için batchSwap işlevini sunmaktadır.
Balancer havuzlarındaki bir flash swap, bir takas gerçekleştirmek için geleneksel olarak gereken girdi token'larını elde tutma zorunluluğunu ortadan kaldırır. Bunun yerine, bir dengesizlik tespit edildiğinde Vault'a takas işlemini gerçekleştirmesini ve ardından ödülü almanızı sağlayabilirsiniz.
0x1.1 Balancer'daki Çeşitli Havuzlar
Aşağıda, bu güvenlik açığıyla ilgili bazı havuz kavramlarını kısaca tanıtıyoruz.
-
Linear Pool'lar:
Linearpool'lar [2], bilinen bir döviz kuru üzerinden bir varlığın ve onun sarılmış, getiri sağlayan karşılığının takasını kolaylaştıran Balancer havuzlarıdır. Adından da anlaşılacağı üzereLinearPool'lar Doğrusal Matematik kullanır. Birlinearpool şu üç token'ı barındırır:- eşit değerde temel token'a sahip iki varlık, yani
mainvewrappedtoken'lar; - karşılık gelen
BPT(Balancer Pool Token).BPT'lerin ERC-20 token'ları olduğunu unutmayın.
- eşit değerde temel token'a sahip iki varlık, yani
-
Linear Pool'ları İç İçe Yerleştirme: Linear Pool BPT'leri başka bir pool'un içine yerleştirilebilir. Bu durum, takasçıların
BPT'den Linear Pool'un temel token'larından birine takas yapabilmesi sayesinde temel varlıklar ile dış havuzdaki token'lar arasında basit birbatchSwapyolu oluşturur. -
Composable Stable Pool'lar: Composable Stable Pool'lar [3], eşitliğe yakın veya bilinen bir döviz kuru üzerinden tutarlı biçimde takas edilmesi beklenen varlıklar için tasarlanmıştır. Composable Stable Pool'lar, benzer ve ilişkili türdeki takaslar için sermaye verimliliğini büyük ölçüde artırarak önemli miktarda fiyat etkisiyle karşılaşmadan büyük ölçekli takasların yapılmasına olanak tanıyan Kararlı Matematik kullanır.
Bir pool, kendi LP token'ına gelen ve giden takasları desteklediğinde composable (birleştirilebilir) olarak nitelendirilir. LP token'ının başka pool'lara yerleştirilmesi ("nesting") sayesinde iç içe geçmiş pool token'larından dış havuzdaki token'lara kolay
batchSwapyapılabilir. -
Boosted Pool'lar:
Boostedpool'lar [4], büyük havuzlar için atıl likiditenin sermaye verimliliğini artırmak amacıyla tasarlanmıştır.Boostedpool'lar aslında diğer pool'ların bir alt sınıfıdır. Örneğin birboostedpool,linearpool'ların üzerine inşa edilebilir.Boosted Pool'lar, kullanıcıların atıl token'ları harici protokollere yönlendirirken yaygın token'lar için takas likiditesi sağlamasına olanak tanıyarak yüksek sermaye verimliliği sunmak için tasarlanmıştır. Bu sayede likidite sağlayıcıları, topladıkları takas ücretlerinin yanı sıra Aave gibi protokollerin avantajlarından da yararlanır.
0x1.2 Güvenlik Açığı Bulunan Boosted Pool'lara Somut Bir Örnek: Balancer Boosted Aave USD
Balancer Boosted Aave USD (sembol: bb-a-USD), atıl likiditesini Aave'ye yönlendirirken üç stablecoin (USDC, USDT ve DAI) arasındaki takasları kolaylaştıran bir Composable Stable Pool'dur. Temel linear pool'lar şunlardır:
bb-a-USDC(USDC ve sarılmış aUSDC'den oluşur)bb-a-USDT(USDT ve sarılmış aUSDT'den oluşur)bb-a-DAI(DAI ve sarılmış aDAI'den oluşur)
Özellikle bb-a-USD, her biri DAI, USDC ve USDT gibi ilişkili bir stablecoin'e sahip üç farklı linear pool'un havuz token'larını içeren bir Composable Stable Pool'dan oluşan bir koleksiyondur. Resmi belgede [5] yer alan aşağıdaki şekil, bb-a-USD'nin yapısını göstermektedir:

0x1.3 BPT Fiyatı Nasıl Hesaplanır
Doğal olarak ortaya çıkan önemli bir soru, belirli bir miktarda (yani amountIn) BPT'yi başka bir token'ın belirli bir miktarıyla (yani amountOut) takas ederken BPT'nin fiyatının nasıl belirleneceğidir.
Balancer, farklı havuzlar tarafından benimsenen matematiksel formüller için ayrıntılı açıklamalar sunmaktadır [6, 7]. Basitlik adına, burada en ilgili kavramları soyutlayarak özetliyoruz.
linear pool örneğini ele alırsak, BPT'nin fiyatı LinearPool sözleşmesinin onSwap işlevinde hesaplanmaktadır.

Hesaplama şu şekilde özetlenebilir:

Burada tokenRate şu formülle hesaplanmaktadır:

sabit bir değerdir: .
Yukarıdaki formülde pay, main token bakiyesi ile wrapped token bakiyesinin toplamına indirgenebilirken payda, önceden tanımlanmış bir değer (yani _INITIAL_BPT_SUPPLY) ile BPT bakiyesi arasındaki farktır.
Farklı token'ların farklı ondalık basamaklara sahip olabileceği göz önünde bulundurulduğunda, hesaplama gerçekleştirilmeden önce tüm ilgili token'ların bakiyelerinin normalize edilmesi gerektiğini belirtmek gerekir. Özellikle, belirli bir token'ın ham bakiyesi, _scalingFactors işlevi tarafından belirlenen ilgili ölçeklendirme faktörüyle çarpılacaktır.
(1) Linear Pool'ların Ölçeklendirme Faktörleri
Hem BPT hem de main token, düzenli ve sabit bir ölçeklendirme faktörüne sahiptir.

(2) bb-a-USD Gibi Boosted Pool'ların Ölçeklendirme Faktörleri
Bir boosted pool'un hesaplaması biraz daha karmaşıktır. Özellikle, döndürülen ölçeklendirme faktörü ham ölçeklendirme faktörünün (ör. 1e18) token oranıyla çarpımıdır ve varsa önbelleğe alınmış token oranından elde edilir.

Önbelleğe alınmış token oranı nereden gelmektedir? _updateTokenRateCache adlı özel bir işlev mevcuttur. Bu işlev, önce ilgili token'ın getRate işlevini çağırarak oranı alır ve ardından bunu önbelleğe kaydeder.

Yine bb-a-USDC örneğini ele alırsak, karşılık gelen getRate işlevinin temel mantığı, daha önce ele aldığımız formülü izlemektedir.

_updateTokenRateCache işlevini tetikleyebilecek üç olası yolun bulunduğunu belirtmek gerekir:

Bunun yanı sıra, onSwap işlevi aracılığıyla gerçekleştirilen güncellemelerde bir son kullanma tarihi kontrolü uygulanmaktadır:

0x2 Güvenlik Açığı Analizi
Temel neden, linear pool'un onSwap işlevindeki aşağı yuvarlama mantığından kaynaklanan fiyat manipülasyonunda yatmaktadır. Bu durum, boosted pool tarafından kullanılan önbelleğe alınmış token oranını olumsuz biçimde etkilemektedir.
Özellikle _downscaleDown işlevi çağrıldığında amountOut aşağı yuvarlanmaktadır. Bu nedenle, amountOut ile scalingFactors[indexOut] arasında önemli bir büyüklük farkı varsa _downscaleDown işlevinin döndürdüğü değer sıfır olabilir.

Örneğin, bb-a-USDC havuzunda USDC (main token) ile takas yapmak için bb-a-USDC (BPT olarak) kullanırsak, amountOut 1.000.000.000.000'dan küçük olduğunda döndürülen değer her zaman sıfıra yuvarlanacaktır. Bu durum, tek yönlü bb-a-USDC likiditesi ekleme işlemi olarak değerlendirilebileceğinden bb-a-USDC bakiyesini artıracaktır.
Sonuç olarak, takas için kullanılan token BPT ise pay aynı kalırken payda azaldığından, oranı hesaplama formülüne paralel biçimde oranı yükselecektir. Bu hata, (büyük) bir fiyat farkına yol açmak için istismar edilebilir.
0x3 Saldırı Analizi
Saldırı işlemi aşağıdaki saldırı adımlarından oluşmaktadır:
- Aave'den Flashloan aracılığıyla 300.000 USDC borç alma.
- bb-a-USDC havuzunda 1,067753 USDC'yi 0,970495 aUSDC ile takas etme.
bb-a-USDCvebb-a-USDhavuzlarındabatchSwapgerçekleştirme, yani 42.203USDCkarşılığında 15.628bb-a-USDC, 139.431bb-a-DAIve 248.868bb-a-USDTelde etme. Ayrıntılı adımlar aşağıdaki tabloda (ondalıklarla birlikte) özetlenmiştir:

- LP token'larını karşılık gelen temel stablecoin'lerle takas etme:
- 139.431
bb-a-DAI->bb-a-DAIhavuzunda 141.127DAI - 15.628
bb-a-USDC->bb-a-USDChavuzunda 15.685USDC - 248.868
bb-a-USDT->bb-a-USDThavuzunda 253.461USDT
- Flashloan'ı geri ödeme ve nihai kâr:
- 114.324
DAI - 253.461
USDT - 0,970495
aUSDC
Saldırganın adım 2'de bb-a-USDC havuzundan USDC ile aUSDC çektiğini belirtmek gerekir; bu durum, adım 3'teki fiyat manipülasyonunu çok daha kolay hale getirmektedir; yani saldırgan yalnızca USDC ve bb-a-USDC'ye odaklanmak durumunda kalmaktadır.
Burada adım 3 kilit bir rol oynamaktadır. Şimdi saldırganın neden kâr elde edebildiğini anlamak için bu adımın ayrıntılarını inceleyelim. Özellikle:
- Adım 3.1,
bb-a-USDChavuzundanbb-a-USDCileUSDCçekmek için kullanılmaktadır; - Adımlar 3.3 ve 3.4,
bb-a-USDC'yibb-a-DAIile takas etmek için kullanılırken adım 3.5,bb-a-USDC'yibb-a-USDTile takas etmek için kullanılmaktadır. - Adım 3.7,
bb-a-USDChavuzundanUSDCilebb-a-USDCtakas etmek için kullanılmaktadır.
Adımlar 3.2 ve 3.6, daha önce ele alınan aşağı yuvarlama nedeniyle herhangi bir hedef token (yani
USDC) iade etmemektedir; dolayısıyla hedef token bakiyeleri takasın ardından değişmemekte ve bu durumbb-a-USDChavuzuna ekstrabb-a-USDClikiditesi ekleme işlemi olarak değerlendirilebilmektedir.
Anormal takaslar ağırlıklı olarak adımlar 3.4, 3.5 ve 3.7'de gerçekleşmektedir. Aşağıda bu adımların her birinin ayrıntılarını sırasıyla inceleyeceğiz.
(1) bb-a-USDC -> bb-a-DAI
Adım 3.3'te bb-a-USDC ile bb-a-DAI arasındaki döviz kuru neredeyse 1 iken adım 3.4'te bu oran 19'a yükselmektedir:
- Adım 3.3: 1.000.339.378.515.783.699 / 1.000.000.000.000.000.000 = 1,00
- Adım 3.4: 139.430.482.942.020.211.267.110 / 7.300.000.000.000.000.000.000 = 19,10
Daha önce ele aldığımız kod mantığını hatırlarsak, adım 3.3'te önceden önbelleğe alınmış token oranı ölçeklendirme faktörünü hesaplamak için döndürüldükten sonra (1.012.181.365.780.643.700) yeni bir değer hesaplamak için güncellenmektedir (40.240.000.000.000.000.000). Bu güncellenen değer, adım 3.4'te yeni ölçeklendirme faktörü olarak kullanılmaktadır. Ham ölçeklendirme faktörleri değişmediğinden (yani 1e18), bu durum yeni oranın eski orandan yaklaşık 40 kat daha büyük olduğu anlamına gelmektedir.

Peki bu önemli artış nereden kaynaklanmaktadır? tokenRate hesaplama formülüne geri dönelim. aUSDC bakiyesi adım 2'de tükendiğinden tokenRate hesaplaması şu şekilde basitleştirilebilir:


Burada nominalMainBalance'ın gerçek değeri, adım 3.2'deki aşağı yuvarlama işleminden kaynaklanmaktadır.
(2) bb-a-USDC -> bb-a-USDT
Adım 3.5, daha fazla bb-a-USDT elde etmek için aynı yöntemi kullanmakta olup bb-a-USDC ile bb-a-USDT arasındaki döviz kuru 12'nin üzerindedir:
- 248.868.905.733.352.246.491.156 / 20.000.000.000.000.000.000.000 = 12,44
(3) USDC -> bb-a-USDC
Ayrıca bptBalance adım 3.6'da artırılmakta, ardından adım 3.7'de bptSupply sıfıra düşmektedir. Bu sayede USDC'yi bb-a-USDC ile neredeyse 1:1 oranında takas etmek mümkün hale gelmektedir.

0x4 Saldırıların ve Kârların Özeti
Bu yazının kaleme alındığı sırada, gerçek dünyada onlarca saldırı gözlemledik ve bu saldırılar 2,12 milyon doları aşan kayıplara yol açtı. Özetle, bu saldırılar üç farklı hesap tarafından gerçekleştirilmiştir:

Balancer, bu güvenlik açığı nedeniyle toplam ~1 milyon dolar kayıp yaşadı. Balancer'a yönelik ilk saldırıdan 12 saatten kısa bir süre sonra, fork protokolü Beethoven X de benzer saldırılara maruz kaldı ve tahminen ~1,1 milyon dolar kayıp yaşadı. Beethoven X, Balancer'dan dahi büyük kayıplar yaşadı! Bu güvenlik olayından kaynaklanan kümülatif kayıp ~2,12 milyon dolar olarak gerçekleşti.
Bu saldırı işlemlerinin tam listesi, hazırladığımız bir belgede derlenmiştir. Daha ayrıntılı bilgi için lütfen bu belgeye başvurunuz.
Saldırganlar Hakkında Bazı Gözlemler
Her ağ tarafından başlatılan işlemleri analiz ettiğimizde, Fantom üzerindeki saldırı işlemlerinin izinin Ethereum ve Optimism üzerindekilerden önemli ölçüde farklı olduğunu tespit ettik.
Özellikle, temel işlevlerdeki kayda değer farklılıkların ötesinde, Fantom'daki saldırgan MEV Bot'ları tarafından ön çalıştırılmamak için iki özgün yöntem kullandı. Ayrıca Fantom üzerindeki saldırıda kullanılan fonlar, saldırıdan 163 gün önce hazırlandı.
Yukarıda ayrıntılı olarak sunulan gözlemlerden şu sonuçları çıkarabiliyoruz:
-
En az iki farklı saldırgan yer almaktaydı.
-
Fantom'daki saldırgan deneyimli bir alışkın suçludur.
0x5 Sonuç
Özetle, bu, aşağı yuvarlama mantığına dayanan ince bir güvenlik açığıdır. Ancak bu güvenlik açığını istismar etmek kolay değildir. Özellikle saldırgan, linear pool'daki aşağı yuvarlama sorununu istismar ederek önbelleğe alınmış token oranını şişirmeyi ve böylece karşılık gelen boosted pool'daki token fiyatını manipüle etmeyi başardı.
Bu olay aynı zamanda, güvenlik açığı içeren kaynaktan fork yapan projelere zamanında bildirim yapılmasının önemini vurgulamaktadır. Balancer'ın uyarısına karşın fork protokollerini hedef alan saldırılar devam etmekte; bu durum, söz konusu projelerin kaynak projelerinden gelen güvenlik güncellemelerini yakından takip etmesi gerektiğini ortaya koymaktadır. Ancak bu fork projelerinin zamanında bilgilendirilmesinin sağlanması, topluluk için süregelen bir zorluk olmaya devam etmektedir.
Bunun yanı sıra, süregelen saldırı dizisi, olası kayıpları etkin biçimde azaltmaya yardımcı olabilecek proaktif tehdit önlemenin önemini bir kez daha gözler önüne sermektedir.
Referans
- [1] https://docs-v2.balancer.fi/concepts/overview/basics.html
- [2] Linear Pool'lar: https://docs-v2.balancer.fi/concepts/pools/linear.html
- [3] Composable Stable Pool'lar
- [4] Boosted Pool'lar
- [5] https://docs-v2.balancer.fi/concepts/pools/boosted.html#example
- [6] https://docs-v2.balancer.fi/reference/math/linear-math.html
- [7] https://docs-v2.balancer.fi/reference/math/stable-math.html



