Anchor adalah kerangka pengembangan paling utama dalam ekosistem Solana. Ia secara besar-besaran mengurangkan rintangan pengembangan melalui ciri-ciri seperti pengesahan akaun deklaratif, pengurutan automatik, dan pemeriksaan keselamatan lalai. Walau bagaimanapun, semasa memberikan kemudahan, kerangka ini secara senyap menyuntikkan beberapa arahan dalaman ke dalam setiap program yang mungkin tidak diketahui oleh pengembang. Arahan "tersembunyi" ini boleh dimanfaatkan oleh penyerang dalam keadaan tertentu, menyebabkan kerugian dana yang serius.
Pasal ini oleh pasukan keselamatan Beosin akan mengungkap corak lubang keamanan penting: dalam versi Anchor yang lebih lama, apabila pembangun menggunakan AccountInfo untuk menyatakan akaun PDA yang dimiliki oleh program, penyerang boleh mengambil alih kawalan akaun dan mengosongkan semua SOL di dalamnya hanya dengan dua langkah menggunakan arahan IDL yang diinject secara automatik oleh Anchor, tanpa memerlukan sebarang keistimewaan.
Satu, Analisis arahan IDL dan mekanisme berkaitan
1.1 Arahan IDL
Anchor secara lalai akan menyuntikkan satu set arahan IDL (Interface Definition Language) ke dalam setiap program, kecuali ciri no-idl diaktifkan secara eksplisit semasa pembinaan. Arahan-arahan ini termasuk:
IdlCreateAccount:Mencipta akaun IDL di rantai
IdlWrite: Menulis data ke akaun IDL / akaun buffer
IdlSetAuthority: Mengubah authority akaun IDL
IdlCloseAccount: Tutup akaun IDL dan pindahkan semua lamports ke penerima yang ditentukan
IdlResizeAccount: Menyesuaikan saiz akaun IDL
IdlCreateBuffer: Mencipta akaun buffer IDL (IDL Buffer)
IdlSetBuffer: Menimpa akaun IDL rasmi dengan data daripada akaun buffer
Perintah-perintah ini dirancang khusus untuk pengurusan IDL di rantai, tetapi perintah-perintah ini mempunyai kemampuan khas terhadap akaun yang dimiliki oleh program (membaca dan menulis data, mengubah pemilik, menutup dan memindahkan lamports) — inilah inti kelemahan tersebut. Penyerang tidak perlu memanggil sebarang perintah perniagaan yang ditulis oleh pembangun; mereka boleh terus memanggil perintah-lalai ini.
1.2 Akaun penyangga IDL
Akaun penyangga IDL (IDL Buffer) ialah akaun sementara yang diperkenalkan oleh Anchor untuk muat naik data IDL besar secara berperingkat. Oleh kerana IDL penuh (ditekan dalam format JSON) mungkin melebihi had saiz satu transaksi, Anchor membenarkan pembinaan buffer terlebih dahulu melalui IdlCreateBuffer, diikuti dengan penulisan bertahap menggunakan beberapa IdlWrite, dan akhirnya diserahkan sekali gus melalui IdlSetBuffer.
Titik utama ialah struktur data akaun IDL / akaun buffer: ia bermula dengan kepala dengan susunan tetap, yang mengandungi medan authority: Pubkey (disebut sebagai controller dalam output ujian artikel ini). Arahan IDL menggunakan medan ini untuk menentukan “siapa yang berkuasa mengendalikan akaun tersebut”.
Namun, masalahnya ialah: logik pemprosesan IdlCreateBuffer pada versi Anchor yang lebih lama akan menginisialisasi mana-mana akaun yang dimiliki oleh program ini sebagai akaun penyangga, dan menetapkan kuasa secara langsung kepada penandatangan transaksi. Dengan kata lain, sekiranya pemilik akaun adalah program ini (contohnya, PDA simpanan program), penyerang boleh menetapkan diri mereka sebagai pengawas akaun tersebut, kemudian menggunakan IdlCloseAccount untuk secara sah memindahkan semua SOL dari akaun tersebut.
1.3 Syarat pemicu lubang keamanan
Pengaktifan lubang ini memerlukan pemenuhan syarat-syarat berikut secara serentak:
- Menggunakan versi Anchor yang lebih lama: Arahan IDL yang tidak mencukupi dalam pengesahan akaun membenarkan akaun perniagaan disalahanggap sebagai akaun IDL.
- no-idl tidak diaktifkan: program mengekalkan entri arahan IDL yang disuntik secara lalai semasa pembinaan, membuka permukaan serangan.
- Menggunakan AccountInfo untuk menyatakan PDA yang dimiliki oleh program: Pembangun menggunakan AccountInfo mentah untuk membawa akaun dana (seperti PDA gudang), tetapi kekurangan discriminator / pemilik yang disediakan oleh akaun bertype Anchor (seperti Account), menjadikan akaun ini tidak dapat dibezakan daripada akaun IDL dalam arahan IDL.
- Akaun dimiliki dan dipegang oleh program dengan lamports: owner == program ini adalah syarat agar arahan IDL boleh mengendalikannya; hanya mempunyai SOL yang memberikan nilai untuk dikosongkan.
Setelah memenuhi syarat-syarat di atas, penyerang hanya memerlukan dua transaksi biasa: terlebih dahulu IdlCreateBuffer untuk mengambil alih controller, kemudian IdlCloseAccount untuk memindahkan semua SOL, sehingga serangan dapat dilakukan tanpa memerlukan keizinan apa pun dari program sasaran.
1.4 Rantai Serangan
Di bawah ini, dengan menggabungkan pelaksanaan dalam Anchor, kita akan menguraikan secara bertahap bagaimana penyerang hanya menggunakan dua arahan bawaan untuk menyamar sebagai akaun IDL dan mengosongkannya.
Gambaran keseluruhan proses serangan
Langkah 1 IdlCreateBuffer(treasury, signer = attacker)
└─ treasury.controller ==> attacker (menjadi pengawas)
Langkah 2 IdlCloseAccount(treasury, authority = attacker, dest = attacker)
└─ treasury.lamports ==> attacker (menarik dana dari perbendaharaan)
Langkah pertama: IdlCreateBuffer mengambil kuasa
Anchor melaksanakan dalaman secara kasar seperti berikut:
#[derive(Accounts)] pub struct IdlCreateBuffer {
#[account(zero)] // ← Key: akaun dengan discriminatornya semua 0 pub buffer: Account,
pub authority: Signer,}
pub fn idl_create_buffer(ctx: Context) -> Result
{
let idl = &mut ctx.accounts.buffer; idl.authority = *ctx.accounts.authority.key; // tetapkan serangan sebagai otoriti Ok(())}
Maksud #[account(zero)] ialah: menerima akaun yang discriminator-nya semua sifar dan dimiliki oleh program, sebagai akaun IDL yang belum inisialisasi. Vault memenuhi kedua-dua syarat ini:
Status vault
Syarat
dimiliki oleh program
setelah init_if_needed, pemilik ditetapkan kepada program ini
discriminator semua sifar
Menggunakan jenis AccountInfo, Anchor tidak menulis discriminator, data semua sifar
Maka penyerang memasukkan vault ke dalam IdlCreateBuffer selepas itu:
- Anchor menulis discriminator IdlAccount ke 8 bait pertama sebelum vault;
- Masukkan kunci awam penyerang ke dalam bidang authority.
Dengan ini, vault telah "disamarkan" sebagai IdlAccount dengan penyerang sebagai otoriti—dan SOL di dalamnya tidak berubah sedikit pun.
Langkah kedua: IdlCloseAccount —— Kosongkan dana
#[derive(Accounts)] pub struct IdlCloseAccount { #[account(mut, has_one = authority)] // ← semak authority == penandatangan pub account: Account, pub authority: Signer, #[account(mut)] pub destination: AccountInfo, // ← dompet serangan}
Vault pada masa ini telah sepenuhnya lulus pengesahan:
- discriminator sepadan dengan IdlAccount
- bidang authority = kunci awam penyerang
- has_one = autoriti disahkan
Oleh itu, semua lamports (SOL yang disetorkan oleh pengguna) dalam vault ditransfer secara sah ke akaun penyerang, menjadikan vault kosong.
Sebab asas:
Menggunakan AccountInfo dalam deposit.rs berbanding dengan Anchor Account yang berjenis adalah punca utama kelemahan ini:
(1) Akaun dengan jenis tertentu (seperti Account) akan menulis discriminator 8 bait khas struktur tersebut semasa inisialisasi;
(2) Setelah discriminator sendiri ditulis, Anchor tidak lagi boleh menganggapnya sebagai IdlAccount (discriminator tidak sepadan), sehingga IdlCreateBuffer pada langkah pertama akan gagal, dan rantai serangan terputus.
Dua, Analisis Kes
Ujian berikut dibina berdasarkan kontrak jambatan lintas rantai PoC yang dibina pada Anchor 0.31.0. Ujian mensimulasikan sebuah dana jambatan (Bridge Treasury) sebenar, di mana pemiliknya ialah program jambatan itu sendiri, yang mengandungi 1.001281 SOL yang disetorkan oleh pengguna. Dompet penyerang awalnya memegang 2 SOL dan tidak memiliki sebarang hak istimewa.
Projek
Nilai / Penerangan
Program Jambatan
CJutNlr8d3oxdb11ReOLaZd5jPsqUxgwt2mv5e7equtE
Akaun Baitul Mal (Treasury)
DxGkzaMhP5Wy824GhoehErdD6MudfuEPGPwaV2s77FsM
Pemilik KuCoin
Program-owned PDA
Saldo awal KuCoin
1.001281 SOL (dana yang disetorkan pengguna)
Controller awal
0x0000...0000 (semua sifar, tidak ditetapkan)
Saldo awal dompet penyerang
2.000000 SOL
Hak yang diperlukan
TIDAK ADA (tidak memerlukan kebenaran apa-apa)
Jumlah transaksi
2 transaksi
Baki akhir KuCoin
0.000000 SOL (dikosongkan)
Penyerang mendapat keuntungan
+1.001281 SOL
Skrin ranah PoC (tests/poc-idl-hijack.ts):
Dapat dilihat, IdlCreateBuffer pada Langkah 1 menjadikan penyerang sebagai pengendali gudang (baki tidak berubah); IdlCloseAccount pada Langkah 2 memindahkan seluruh 1.001281 SOL dari gudang ke dompet penyerang, menjadikan baki gudang sifar.
Cadangan pembaikan/penjagaan:
(1) Naik taraf versi Anchor: Versi terkini telah memperbaiki masalah ini (arahan IDL akan membezakan dengan ketat akaun IDL dan akaun perniagaan); mengelakkan penggunaan versi Anchor yang lebih lama adalah langkah pertahanan paling langsung dan paling asas.
(2) Aktifkan no-idl semasa pembinaan: Matikan secara eksplisit penyuntikan arahan IDL untuk program persekitaran pengeluaran, menghapuskan permukaan serangan ini dari sumbernya.
(3) Gunakan akaun berjenis daripada AccountInfo telanjang: Gunakan akaun dana yang dibawa oleh Account / SystemAccount dengan pemeriksaan discriminator dan owner untuk mengelakkan ia disalahertikan oleh arahan IDL.
(4) Meminimumkan akaun yang boleh dikosongkan oleh program: Tambahkan sekatan pemilik / biji / pembez yang jelas kepada PDA yang menyimpan dana, dan semak pembez akaun dalam arahan penting.
Penutup
Kerentanan ini pada dasarnya adalah kombinasi “arahan tersembunyi kerangka kerja + kegagalan pengenalpastian jenis akaun”. Pembangun harus menyedari bahawa arahan IDL yang disuntikkan secara lalai oleh Anchor adalah permukaan serangan yang sebenar, dan dengan meningkatkan versi kerangka kerja serta menerapkan sekatan jenis ketat pada akaun dana, risiko penarikan dana tanpa kebenaran semacam ini boleh dielakkan secara berkesan.
Beosin ialah syarikat teknologi keselamatan blockchain dan kepatuhan peraturan terkemuka, yang berfokus pada audit keselamatan kontrak pintar sebelum pelancaran projek, pemantauan dan penghalangan risiko keselamatan semasa operasi projek, pemulihan aset yang dicuri, anti pencucian wang (AML) aset maya, serta penyiasatan dan pelacakan. Beosin telah menyediakan produk kepatuhan blockchain "sehenti" dan perkhidmatan keselamatan kepada 200 lebih penyedia aset maya dan 4,500 lebih projek Web3 di lebih 20 negara dan wilayah di seluruh dunia. Sila klik kotak pesan di saluran kami untuk menghubungi kami.

