latar belakang

Pada tanggal 22 Agustus, Balancer secara resmi mengumumkan bahwa mereka telah menerima laporan kerentanan serius yang memengaruhi beberapa pool V2 Boost. Hanya 1,4% TVL yang terpengaruh. Beberapa pool telah ditangguhkan, dan pengguna telah diberitahu untuk menarik LP likuiditas sesegera mungkin. [1] [2]

Pada tanggal 27 Agustus, sistem SlowMist MistEye menemukan transaksi serangan yang diduga mengeksploitasi kerentanan Balancer. [3]

Karena pool tidak dapat dihentikan sementara, sebagian dana masih terpengaruh oleh serangan tersebut. Pejabat Balancer sekali lagi mengingatkan pengguna untuk menarik LP dari pool yang terpengaruh. [4] Selanjutnya, Balancer secara resmi merilis rincian kerentanan yang diungkapkan pada bulan Agustus di Medium [5]. Tim keamanan SlowMist meninjaunya dan rinciannya adalah sebagai berikut:

Perkenalan

Pejabat Balancer hanya menunjukkan dalam pengungkapan mereka bahwa masalah saat ini adalah pembulatan ke bawah dari kumpulan linier dan pasokan virtual dari kumpulan yang dapat dikomposisi menyebabkan bptSupply menjadi 0. Pertama, mari kita lihat sekilas konten dalam protokol Balancer yang terkait dengan kerentanan ini.

Gudang Penyeimbang V2

Protokol Balancer V2 [6] adalah protokol pembuat pasar otomatis (AMM) terdesentralisasi berbasis Ethereum yang mewakili blok bangunan fleksibel untuk likuiditas yang dapat diprogram. Komponen intinya adalah kontrak Vault, yang menyimpan catatan semua kumpulan dan mengelola akuntansi dan transfer token, bahkan pengemasan dan pembongkaran ETH asli. Dengan kata lain, implementasi Vault memisahkan akuntansi dan manajemen token dari logika pool.

Ada empat antarmuka dalam Vault, yaitu joinPool, exitPool, swap dan batchSwap (join, exit dan swap adalah panggilan terpisah dan tidak ada kombinasi dalam panggilan tunggal). Salah satu fitur yang menonjol adalah batchSwap, yang memungkinkan beberapa pertukaran atom antara beberapa kumpulan, menghubungkan keluaran dari satu pertukaran kumpulan ke masukan dari kumpulan lain (GiveIn dan GiveOut). Sistem ini juga memperkenalkan flash swap [7], yang merupakan bentuk pinjaman kilat internal.

Kolam Renang Linear

Untuk meningkatkan efisiensi modal LP dan memecahkan masalah overhead warp dan unwarp yang tinggi, Balancer meluncurkan pool linear sebagai solusi di V2, dengan demikian memperkenalkan token BPT (ERC20 Balancer Pool Token).

Linear Pool [8] terdiri dari token utama, token yang diwarp, dan token BPT, yang saling bertukar aset dan token yang dibungkus dan menghasilkan yield pada nilai tukar yang diketahui. Semakin tinggi persentase token yang dibungkus, semakin tinggi pula hasil dan efisiensi modal dari kumpulan tersebut. Selama proses warp, faktor skala biasanya digunakan untuk memastikan bahwa token yang berbeda dihitung dengan akurasi yang sama.

Kolam Renang yang Dapat Disusun

Semua kumpulan Balancer adalah kumpulan yang dapat dikomposisi, yang berisi token lain dan kumpulan itu sendiri juga memiliki tokennya sendiri. Di antara semuanya, koin BPT merujuk pada token kumpulan saldo ERC20, yang menjadi dasar semua kumpulan. Pengguna dapat bebas menggabungkan token BPT untuk ditukarkan di pool lain. Pertukaran selalu melibatkan satu kumpulan dan dua token: GiveIn dan GiveOut. Masuk berarti mengirim token penyusun dan menerima BPT, sementara Keluar berarti mengirim BPT dan menerima token penyusun. Jika BPT sendiri merupakan token penyusunnya, ia dapat dipertukarkan seperti token lainnya. Implementasi semacam itu merupakan jalur batchSwap sederhana antara aset dasar dan token di pool eksternal, di mana pengguna dapat menggunakan BPT untuk menukar aset dasar dari pool linier, yang juga merupakan dasar dari Balancer Boosted Pool [9].

Melalui kombinasi di atas, kumpulan komposisi Balancer terbentuk. Kumpulan stabil yang dapat disusun bb-a-USD terdiri dari tiga kumpulan linier yang mengirimkan likuiditas menganggur ke protokol eksternal (Aave). Misalnya, bb-a-DAI adalah kumpulan linier yang berisi DAI dan waDAI (aDAI terbungkus). Saat pengguna perlu melakukan batchSwap (misalnya untuk menukar USDT dengan DAI), jalur pertukarannya adalah sebagai berikut:


1. Di pool linier USDT, tukarkan USDT dengan bb-a-USDT (masuk ke pool linier USDT);

2. Dalam bb-a-USD, bb-a-USDT ditukar dengan bb-a-DAI (pertukaran linier antara BPT);

3. Pada kumpulan linier DAI, bb-a-DAI ditukar dengan DAI (keluar dari kumpulan linier DAI).

Setelah memahami secara singkat prasyarat pengetahuan di atas, kita memasuki fase analisis kerentanan.

menganalisa

Pada tanggal 27 Agustus, tim keamanan SlowMist menerima pesan dari sistem MistEye yang mengidentifikasi dugaan kerentanan Balancer yang sedang dieksploitasi secara luas. Transaksi [3] adalah sebagai berikut:

Penyerang pertama kali meminjam 300.000 USDC dari AAVE melalui pinjaman kilat. Kemudian, operasi batchSwap dari Vault dipanggil untuk melakukan kalkulasi pertukaran token BPT melalui kumpulan stabil gabungan bb-a-USD, dan akhirnya 94.508 USDC ditukar dengan 59.964 bb-a-USDC, 68.201 bb-a-DAI, dan 74.280 bb-a-USDT. Akhirnya, token BPT yang diperoleh dikeluarkan dari kumpulan melalui exitPool kontrak Vault dengan imbalan aset dasar, pinjaman kilat dilunasi, dan keuntungan sekitar US$108.843,7 tersisa.

Dapat dilihat bahwa kunci serangan ini terletak pada batchSwap. Apa sebenarnya yang terjadi di batchSwap? Mari kita lihat lebih dekat.

Selama seluruh proses batchSwap, penyerang pertama-tama menukar USDC dari kumpulan bb-a-USDC, dan kemudian menukar token BPT, menukar bb-a-USDC untuk bb-a-DAI, bb-a-USDT, dan USDC. Terakhir, token utama USDC ditukar dengan bb-a-USDT. Artinya, bb-a-USDC sebagai token BPT utama bertindak sebagai token penyusun GiveOut dan GiveIn.

Pada langkah pertama, penyerang menebus token utama USDC dengan token BPT di kumpulan linier bb-a-USDC dengan faktor skala tetap, dan jumlah yang meningkat dicatat dalam bptBalance di kumpulan tersebut. Namun, setelah pertukaran onSwap kedua, kami menemukan bahwa nilai amountOut USDC yang ditukar selama proses pertukaran yang sama adalah 0. Mengapa demikian?


Jika kita menelaah lebih jauh fungsi onSwap, kita akan menemukan bahwa dalam proses ini, pemrosesan presisi pertama kali dilakukan untuk menominasikan token dan menghitung faktor skala token terkait. Ketika fungsi downscaleDown dipanggil berikutnya, amountOut dibulatkan ke bawah. Jika perbedaan antara amountOut dan scalingFactors[indexOut] besar, nilai downscaleDown yang dihitung adalah nol.

Dengan kata lain, ketika kita menggunakan token BPT untuk menebus token utama, jika amountOut terlalu kecil, nilai pengembalian akan dibulatkan ke bawah menjadi nol, dan nilai ini kurang dari 1e12 yang dihitung oleh scalingFactors. Namun, jumlah bb-a-USDC yang masuk dengan amountIn akan tetap ditambahkan ke jumlah virtual bptBalance, dan operasi ini akan meningkatkan saldo di pool bb-a-USDC, yang dapat dianggap sebagai penambahan likuiditas bb-a-USDC secara sepihak.

Kemudian, dengan memanfaatkan karakteristik kumpulan stabil yang dapat disusun, bb-a-USDC pertama kali ditukar dengan token BPT lainnya melalui konversi timbal balik antara token BPT. Dengan mengikuti proses pertukaran ini, Anda dapat menggabungkan jalur panggilan kumpulan stabil berikut: bb-a-DAI onSwap -> swapGivenIn -> onSwapGivenIn untuk pertama-tama menukar bb-a-USDC menjadi bb-a-DAI dan bb-a-USDT secara bergantian. Tidak seperti pada pool linear, pool stabil yang dapat disusun memerlukan pembaruan nilai tukar yang di-cache sebelum melakukan operasi onSwap. Dari kode tersebut, kita dapat melihat bahwa dalam kumpulan gabungan, onSwap pertama-tama akan menentukan apakah nilai tukar token yang di-cache perlu diperbarui.

Setelah pertukaran sebelumnya, jumlah bb-a-USDC telah berubah, dan jumlah total riil setelah nominalisasi melalui _toNominal adalah totalBalance 994.010.000.000, dan pasokan virtual token BPT adalah 20.000.000.000. Dapat dihitung bahwa nilai tukar yang diperbarui hampir 45 kali lipat nilai tukar cache asli kumpulan linier sebelumnya sebesar 1.100.443.876.587.504.549, yaitu 49.700.500.000.000.000.000.

Selanjutnya, bb-a-USDC ditukar dengan USDC di pool linear. Namun, pertukaran ini sama dengan pertukaran kedua, yang sekali lagi menyebabkan amountOut dibulatkan ke bawah menjadi 0, dan jalur pertukarannya sama seperti sebelumnya.

Pertukaran berikutnya adalah mengonversi USDC menjadi bb-a-USDC, dan jalur pertukarannya adalah onSwap -> onSwapGivenIn -> _swapGivenMainIn. Selama proses ini, kami menemukan bahwa saat menghitung jumlah yang perlu ditebus, perhitungan pasokan virtual didasarkan pada perbedaan antara total pasokan token BPT setelah penebusan dan jumlah yang tersisa di kumpulan, yaitu 0.

Hal ini karena bptSupply bernilai 0, dan fungsi _toNominal dipanggil secara langsung saat menghitung BPT Out. Panggilan jalur ini membuat rasio pertukaran USDC terhadap bb-a-USDC mendekati 1:1.

Meringkaskan

batchSwap mengonversi USDC menjadi token BPT melalui sejumlah pertukaran atom antara sejumlah kumpulan, menghubungkan keluaran dari satu pertukaran kumpulan dengan masukan dari kumpulan lain (tokenIn dan tokenOut). Dalam batchSwap ini, tidak ada transfer token aktual yang terjadi, tetapi jumlah pertukaran akhir dikonfirmasi dengan mencatat jumlah transfer masuk dan keluar. Karena kumpulan linier dipertukarkan melalui token aset dasar, metode pertukarannya adalah menghitung Nilai melalui pasokan virtual dan algoritma tetap. Oleh karena itu, ada dua kerentanan keamanan di batchSwap:

Yang pertama adalah masalah pembulatan ke bawah dari kumpulan linear. Penyerang meningkatkan rasio token yang di-cache dengan menambahkan token utama ke kumpulan secara sepihak melalui pembulatan, sehingga memanipulasi nilai tukar token di kumpulan yang dapat disusun terkait.

Kedua, karena karakteristik pasokan virtual dari kumpulan yang dapat disusun, pasokan virtual dihitung dengan mengurangi saldo dalam kumpulan dari token BPT. Jika GiveIn adalah token BPT selama penebusan, bagian ini akan dikurangi dari pasokan berikutnya. Penyerang hanya perlu menebus BPT sebagai GiveIn dan memanipulasi pasokannya menjadi 0 terlebih dahulu, lalu melakukan pertukaran terbalik, yaitu, BPT menjadi pihak GiveOut lagi. Pada saat ini, karena pasokannya 0, algoritme akan melakukan penebusan aktual pada rasio mendekati 1:1, yang lebih rendah dari rasio penebusan kumpulan linier, yang menyebabkan jumlah token BPT GiveOut dimanipulasi secara tidak langsung.

Kita dapat menemukan bahwa kerentanan pertama meningkatkan nilai tukar untuk pertukaran, sementara kerentanan kedua mengurangi nilai tukar dalam pertukaran terbalik. Penyerang memanfaatkan buff ganda untuk mendapatkan keuntungan dan kabur.

Tautan Referensi:

[1]https://twitter.com/Balancer/status/1694014645378724280

[2]https://forum.balancer.fi/t/kerentanan-ditemukan-di-beberapa-kumpulan/5102?u=endymionjkb

[3]https://etherscan.io/tx/0x7020e0ccafff2c86db3df5a2af0cccb4e931fe948f69bf20ea517b0cc99c1f15

[4]https://twitter.com/Balancer/status/1695777503699435751

[5]https://medium.com/balancer-protocol/manipulasi-laju-dalam-pool-yang-ditingkatkan-balancer-postmortem-teknis-53db4b642492

[6]https://docs.balancer.fi/konsep/ikhtisar/basics.html

[7]https://docs.balancer.fi/referensi/swaps/flash-swaps.html#flash-swaps

[8]https://docs.balancer.fi/konsep/pools/linear.html

[9]https://docs.balancer.fi/konsep/pools/boosted.html