Notional Finance Dibobol sebanyak $1.73M Akibat Kerentanan Integer Overflow

iconMetaEra
Kongsi
AI summary iconRingkasan
Serangan sebanyak $1.73 juta menimpa Notional Finance pada 4 September 2026, apabila penyerang memanfaatkan integer overflow dalam kontrak Escrow. Kelemahan dalam fungsi ExchangeRate._convertToETH membolehkan kedudukan fCash dicipta tanpa jaminan yang mencukupi. Insiden ini menekankan kepentingan nisbah risiko-keuntungan yang kuat dalam strategi DeFi. Pedagang yang menggunakan TA untuk kripto harus tetap waspada kerana kelemahan sedemikian boleh mengubah dinamik pasaran dengan cepat.

Penulis: Jiu Jiu

Sunting: 77

Latar belakang

Pada 4 September 2026, platform pinjaman terdesentralisasi terkenal, Notional Finance, diserang, merugikan sekitar USD 1.73 juta. Berikut adalah analisis terperinci oleh pasukan keselamatan Slow Mist mengenai insiden serangan ini:

Pengetahuan asas

Dalam Notional Finance V1, fCash boleh difahami sebagai piutang tunai dengan tarikh jatuh tempo. Penerima tunai akan menerima pembayaran pada tarikh jatuh tempo, manakala pembayar tunai akan membuat pembayaran pada tarikh jatuh tempo. Protokol mencatat kedua-dua posisi ini dalam Portfolio, kemudian menilai kesihatan akaun untuk menentukan sama ada akaun tersebut boleh membuka posisi melalui pemeriksaan barang jaminan bebas.

safeTransferFrom ERC1155 di sini bertindak sebagai fungsi perakaunan aset. Apabila jenis aset yang dimasukkan adalah milik penerima tunai, kontrak akan memanggil Portfolios.mintfCashPair, sambil mencatatkan liabiliti terhadap pembayar dan hak klaim yang sama terhadap penerima. Panggilan ini kelihatan seperti pemindahan ERC1155, tetapi sebenarnya merupakan penciptaan pasangan fCash.

Setelah posisi baru ditulis ke Portfolio, pengiraan dan pemeriksaan barang jaminan bebas perlu dilakukan. Kontrak RiskFramework akan terlebih dahulu mengagregasi liabilitas fCash pembayar menjadi int256 bertanda, kemudian menukar baki setiap mata wang ke ETH untuk diagregasi. Nombor negatif mewakili kas yang perlu dibayar, manakala nombor positif mewakili kas atau hak milik yang dimiliki akaun. Hasil pengiraan ini menentukan sama ada transaksi dibenarkan atau tidak.

Selepas posisi tamat tempoh, Portfolio akan memanggil fungsi portfolioSettleCash dari Escrow untuk menukar fCash yang tamat tempoh menjadi baki tunai. Akaun kemudian akan menarik keluar aset DAI atau USDC yang berkaitan daripada Escrow melalui fungsi penarikan.

Sebab asas

Lubang keselamatan utama dalam serangan ini terletak pada ExchangeRate._convertToETH yang digunakan dalam kontrak pelaksanaan Escrow, di mana satu baris kod secara langsung menukar nilai mutlak nombor bulat bertanda kepada uint128.

Penyerang terlebih dahulu membuka dua posisi hutang dengan payer yang sama, dengan jumlah masing-masing 1 dan uint128.max. Jumlah kedua hutang tersebut tepat sama dengan 2^128. Oleh kerana pembayaran oleh payer dihitung sebagai nombor negatif dalam pengiraan risiko, maka parameter kunci yang dimasukkan ke dalam convertBalancesToETH Escrow ialah -2^128.

Namun, versi Solidity yang digunakan dalam kontrak ini adalah 0.6.x, jadi nilai mutlak 2^128 apabila dipaksa diubah menjadi uint128 akan meluap menjadi 0, sehingga liabiliti ini tidak dimasukkan ke dalam hasil yang dinilai dalam ETH.

Akhirnya, dalam fungsi mintfCashPair, hanya memerlukan jaminan bebas pembayar lebih besar atau sama dengan sifar, maka ia masih boleh lulus pemeriksaan ini selepas berlaku overflow ke sifar, dan akaun yang tidak mempunyai aset yang mencukupi untuk menutupi liabiliti masih boleh membuka kedudukan.

Analisis langkah serangan

1. Dalam transaksi pendahuluan (0xe1589a19…d60a), penyerang pertama-tama menciptakan beberapa kontrak bantuan dan memanggil fungsi setApprovalForAll pada kontrak ERC1155Trade untuk memberikan kuasa kepada kontrak bantuan; selain itu, penyerang juga mengambil maklumat terlebih dahulu mengenai tarikh jatuh tempo dua CashMarket dan mengira nilai tiga parameter AssetId yang diperlukan untuk operasi seterusnya.

2. Seterusnya, penyerang memanggil fungsi safeTransferFrom daripada kontrak ERC1155Trade untuk mencetak pasangan fCash bernilai 1, dengan cashGroupId aset bon sebanyak 2, dan tarikh jatuh tempo timestamp 1788480000 (4 September 2026, pukul 8:00). Fungsi dalaman _upsertAsset akan dipanggil untuk mengemas kini liabilitas dan pendapatan yang dijangka bagi alamat from (kontrak penyerang) dan alamat to (kontrak penerima 1).

Selepas pasangan dicetak, Portfolios akan segera memeriksa jaminan bebas kontrak serangan, dan kerana kuantiti pencetakan pertama terlalu kecil, ia akan dibundarkan menjadi sifar apabila ditukar kepada ETH mengikut kadar semasa dan ketepatan, maka pemeriksaan pertama dapat lulus.

3. Penyerang kemudian memanggil semula fungsi safeTransferFrom daripada kontrak ERC1155Trade untuk mencetak pasangan fCash dengan jumlah uint128.max, di mana cashGroupId bagi aset bon berkaitan ialah 2, tetapi masa tamat tempoh timestamp ialah 1796256000 (3 Disember 2026, pukul 8:00).

Di sini ada butiran kecil: penyerang mencetak aset bon dengan tarikh jatuh tempo yang berbeza ke dua alamat yang berbeza. Ini kerana semasa mengemas kini liabiliti untuk alamat dari, jika aset bon adalah sama, ia akan secara langsung ditambahkan, yang menyebabkan revert semasa pemprosesan penambahan akibat overflow (kontrak menggunakan pustaka safeMath).

4. Selepas memperbaharui liabiliti dan pendapatan yang dijangka untuk alamat dari (kontrak serangan) dan alamat ke (kontrak penerima 2), fungsi mintfCashPair akan memanggil fungsi freeCollateral untuk mengira kedudukan jaminan bersih alamat dari dan memeriksa sama ada ia perlu lebih besar atau sama dengan 0 agar sihat.

Ia akan terlebih dahulu menambahkan jumlah tanggungan dua penerbitan dari alamat from dalam fungsi getRequirement dalam kontrak RiskFramework:

Hasil akhirnya ialah -2^128, kemudian fungsi convertBalancesToETH daripada kontrak Escrow akan dipanggil untuk menukar nilai ini kepada penilaian ETH, dan fungsi convertBalancesToETH pula akan memanggil fungsi _convertToETH daripada pustaka ExchangeRate untuk pemprosesan pengiraan:

Semasa mengikuti ke dalam fungsi _convertToETH, ia dapat dilihat bahawa ia terlebih dahulu mengambil nilai mutlak balance sebelum memanggil uint128 untuk menukar 256-bit kepada 128-bit, manakala jumlah tanggungan dari dari di atas ialah -2^128. Apabila nilai mutlak 2^128 ditukar secara paksa menggunakan uint128(), ia akan melebihi had dan meluap menjadi 0. Ini bermakna protokol salah menganggap posisi jaminan bebas dari alamat from sebagai 0 yang sihat, dengan itu lulus pemeriksaan akhir dalam fungsi mintfCashPair.

5. Selepas menerima kontrak 2, posisinya dibahagikan semula kepada dua kontrak bantuan lain, kedua-dua safeTransferFrom ini masih akan memasuki fungsi mintfCashPair. Namun, kerana pembayar pada masa ini ialah kontrak penerima semasa penerbitan kedua, yang memegang posisi pendapatan yang dijangka besar daripada langkah sebelumnya, pengiraan risiko menganggapnya sebagai piutang positif, sehingga lulus pemeriksaan jaminan, dan dua kontrak bantuan ini kemudian memperoleh receiver fCash yang boleh ditukar kepada tunai setelah jatuh tempo.

6. Penyerang kemudian menjalankan transaksi keuntungan rasmi kedua, di mana kontrak bantuan untuk dua kedudukan yang dipegang dalam transaksi sebelumnya ditutup untuk menyelesaikan hak masing-masing, meningkatkan saldo tunai aset yang berkaitan, sebelum memanggil fungsi withdraw kontrak Escrow untuk menarik aset tersebut sebagai keuntungan.

Ringkasan

Titik utama serangan ini ialah penyerang terlebih dahulu memanfaatkan dua kedudukan terpisah untuk menjumlahkan hutang payer menjadi 2^128, kemudian meminta Escrow menukar hutang ini menjadi ETH. Dengan memanfaatkan lubang keamanan overflow dalam penukaran jenis, hasilnya dipotong menjadi sifar, sehingga pemeriksaan risiko diluluskan.

Pasukan keselamatan SlowMist menyarankan pihak projek untuk melakukan pemeriksaan julat sebelum melakukan penukaran jenis; hasil yang melebihi type(uint128).max harus ditolak secara langsung. Sementara itu, semua sempadan yang melibatkan jumlah bertanda, penskalaan ketepatan, dan nilai aset harus diuji dengan nilai ekstrem, terutama mencakup input seperti uint128.max, uint128.max + 1, dan nilai mutlak nombor negatif. Anda boleh merujuk atau menggunakan pustaka SafeCast OpenZeppelin untuk pemprosesan penukaran.

Penafian: Maklumat yang terdapat pada halaman ini mungkin telah diperoleh daripada pihak ketiga dan tidak semestinya menggambarkan pandangan atau pendapat KuCoin. Kandungan ini adalah disediakan bagi tujuan maklumat umum sahaja, tanpa sebarang perwakilan atau waranti dalam apa jua bentuk, dan juga tidak boleh ditafsirkan sebagai nasihat kewangan atau pelaburan. KuCoin tidak akan bertanggungjawab untuk sebarang kesilapan atau pengabaian, atau untuk sebarang akibat yang terhasil daripada penggunaan maklumat ini. Pelaburan dalam aset digital boleh membawa risiko. Sila menilai risiko produk dan toleransi risiko anda dengan teliti berdasarkan keadaan kewangan anda sendiri. Untuk maklumat lanjut, sila rujuk kepada Terma Penggunaan dan Pendedahan Risiko kami.