Ang Anchor ay ang pinakamalaking development framework sa ecosystem ng Solana. Sa pamamagitan ng mga tampok tulad ng declarative account verification, automatic serialization, at built-in security checks, napakadali nito ang pagbuo. Gayunpaman, habang binibigay ng framework ang kahalagahan, ito ay naglalagay ng ilang internal instructions sa bawat program na maaaring hindi alam ng mga developer. Ang mga "nakatago" na instruction na ito ay maaaring gamitin ng mga attacker sa ilang partikular na kondisyon upang magdulot ng malalaking pagkawala ng pera.
Iipakita ng Beosin Security Team ang isang mahalagang pattern ng vulnerability: sa mga mas mababang bersyon ng Anchor, kapag ginagamit ng developer ang AccountInfo upang ilarawan ang PDA account na may-ari ng program, maaaring mag-atake ang isang hacker gamit ang IDL instruction na awtomatikong inilalagay ng Anchor, at kailangan lamang ng dalawang hakbang upang kunin ang kontrol ng account at walang iwanang SOL, nang walang kailangang magkaroon ng anumang espesyal na pahintulot.
Isa. Pagsusuri sa mga utos ng IDL at kaugnay na mekanismo
1.1 IDL Instruction
Ang Anchor ay magpapaloob ng isang set ng mga utos na IDL (Interface Definition Language) sa bawat programa, maliban kung ang no-idl feature ay aktibado nang eksplisito sa pagbuo. Ang mga utos na ito ay kasama ang:
IdlCreateAccount: Lumikha ng isang chain-based IDL account
IdlWrite: Isulat ang data sa IDL account / buffer account
IdlSetAuthority: Baguhin ang authority ng IDL account
IdlCloseAccount: Isara ang IDL account at ilipat ang lahat ng lamports sa nakatakdang tagatanggap
IdlResizeAccount: I-adjust ang laki ng IDL account
IdlCreateBuffer: Lumikha ng IDL Buffer account (IDL Buffer)
IdlSetBuffer: I-overwrite ang opisyal na IDL account gamit ang data mula sa buffer account
Ang mga utos na ito ay disenyo para sa pamamahala ng chain-based IDL, ngunit may espesyal na kapasidad sa mga akawnt na may-ari ng programa (basahin at isulat ang data, baguhin ang tagapag-ugnay, isara at ilipat ang mga lamports)—ito ang puso ng butas. Hindi kailangan ng attacker na tawagan ang anumang negosyo utos na isinulat ng developer; direktang tatawagin nila ang mga itinakdang utos.
1.2 IDL Buffer Account
Ang IDL Buffer account ay isang pansamantalang account na ipinakilala ng Anchor para sa pag-upload ng malalaking IDL data sa mga bahagi. Dahil ang buong IDL (na compressed sa JSON) ay maaaring lumampas sa limitasyon ng laki ng isang transaksyon, pinapayagan ng Anchor na unang lumikha ng buffer gamit ang IdlCreateBuffer, sumunod ay isulat ito sa mga bahagi gamit ang maraming IdlWrite, at huling i-submit nang isang beses gamit ang IdlSetBuffer.
Ang pangunahing punto ay ang data structure ng IDL account / buffer account: ito ay nagsisimula sa isang header na may fixed layout, na naglalaman ng isang authority: Pubkey field (tinatawag na controller sa test output ng artikulong ito). Ang IDL instruction ay gumagamit ng field na ito upang matukoy kung "sino ang may awtoridad na mag-operate sa account".
Ang problema ay: ang lohika ng IdlCreateBuffer sa mas mababang bersyon ng Anchor ay mag-iinitialize ang anumang account na may-ari ng programa bilang isang buffer account, at direktang i-set ang authority bilang signatory ng transaksyon. Ibig sabihin, kung ang owner ng isang account ay ang programa mismo (halimbawa, ang PDA vault ng programa), ang isang attacker ay makakasulat ng sarili bilang controller nito, at pagkatapos ay gamitin ang IdlCloseAccount upang legal na ilipat ang lahat ng SOL mula sa account.
1.3 Mga kondisyon para sa pag-trigger ng vulnerability
Kailangan ng parehong mga kondisyon upang masira ang butas:
- Gumamit ng mas lumang bersyon ng Anchor: Ang nakapagpapalabas na IDL instruction ay hindi sapat na nagpapahiwatig ng account, na nagpapahintulot na maliit ang business account bilang IDL account.
- Hindi pinagana ang no-idl: ang programa ay nagpanatili ng default na injected IDL instruction entry sa pagbuo, na nagpapakita ng attack surface.
- Gamitin ang AccountInfo upang ipahayag ang PDA na may-ari ng programa: Ang mga developer ay gumagamit ng裸 na AccountInfo upang magdala ng account ng pondo (tulad ng treasury PDA), na kulang sa discriminator / owner na pagkilala na dala ng Anchor-typed account (tulad ng Account), na ginagawa itong “hindi makikilala” sa IDL instruction kumpara sa IDL account.
- Ang account ay may-ari at nagtataglay ng lamports: owner == ang programa ito ay kondisyon para sa IDL instruction na makapag-operate dito; kailangan magkaroon ng SOL upang may halaga na mabawasan.
Pagkatapos matugunan ang mga kondisyon, kailangan lang ng attacker ng dalawang karaniwang transaksyon: una ay IdlCreateBuffer upang kunin ang controller, at pagkatapos ay IdlCloseAccount upang ilipat ang lahat ng SOL, at maaari nilang tapusin ang pag-atake nang walang kailangang bigyan ng anumang pahintulot ang target na programa.
1.4 Chain of Attack
Sa ibaba, kasama ang pagsusuri sa loob ng Anchor, hihiwalay namin ang paraan kung paano ginamit ng attacker ang dalawang nakapaloob na utos lamang upang “magkamali” ang isang karaniwang pautang na vault bilang IDL account at pagkatapos ay buuin ito.
Overview ng Attack Process
Hakbang 1 IdlCreateBuffer(treasury, signer = attacker)
└─ treasury.controller ==> attacker (maging controller)
Hakbang 2 IdlCloseAccount(treasury, authority = attacker, dest = attacker)
└─ treasury.lamports ==> attacker (awasan ang treasury)
Unang hakbang: IdlCreateBuffer ay humahawak ng awtoridad
Ang Anchor ay may panloob na implementasyon na ganito:
#[derive(Accounts)]pub struct IdlCreateBuffer {
#[account(zero)] // ← Key: isang account na may discriminator na lahat 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; // itakda ang attack bilang authority Ok(())}
Ang kahulugan ng #[account(zero)] ay: tumanggap ng isang account na may discriminator na lahat ay zero at binabawasan ng programa, bilang isang hindi pa inisyalisadong IDL account. At ang vault ay sumasapat sa parehong kondisyon:
Kalagayan ng vault
Mga kondisyon
nagmamay-ari ng programa
Pagkatapos ng init_if_needed, ang owner ay itinakda bilang ang programa na ito
discriminator lahat ng zero
Gumagamit ng AccountInfo type, hindi isusulat ng Anchor ang discriminator, ang data ay zero lahat
Kaya isinumite ng attacker ang vault sa IdlCreateBuffer pagkatapos:
- Isulat ang discriminator ng IdlAccount sa mga unang 8 byte bago ang vault;
- Isulat ang public key ng attacker sa field na authority.
Sa point na ito, ang vault ay “nakapagtakip” bilang isang IdlAccount na may authority sa attacker—at ang mga SOL nito ay hindi nababawas.
Pangalawang hakbang: IdlCloseAccount —— Linisin ang mga pondo
#[derive(Accounts)]pub struct IdlCloseAccount { #[account(mut, has_one = authority)] // ← check authority == signer pub account: Account, pub authority: Signer, #[account(mut)] pub destination: AccountInfo, // ← attack wallet}
Ang vault ay naging ganap na nakapagpasa ng pagsusuri:
- magkakatugma ang discriminator sa IdlAccount
- ang field na authority = public key ng attacker
- has_one = authority ay nagsagawa ng pag-verify
Kaya ang lahat ng lamports (lahat ng SOL na ipinapadala ng user) sa vault ay legal na napadala sa account ng attacker, at naging zero ang vault.
Pangunahing dahilan:
Ang paggamit ng AccountInfo sa deposit.rs kaysa sa typed na Anchor Account ay ang patayong ugat ng butas na ito:
(1) Ang mga account na may uri (tulad ng Account) ay sumusulat ng 8-byte na discriminator na espesipiko sa estructura kapag iniihinto ito;
(2) Pagkatapos isulat ang sariling discriminator, hindi na kayang gamitin ng Anchor ang ito bilang IdlAccount (hindi tumutugma ang discriminator), at mabubuo ang unang hakbang na IdlCreateBuffer, at putol ang serye ng pag-atake.
Pangalawa: Pag-aaral ng kaso
Ang sumusunod na pagsubok ay batay sa PoC ng smart contract ng cross-chain bridge na binuo gamit ang Anchor 0.31.0. Ang pagsubok ay nagmumodelo ng isang tunay na bridge treasury kung saan ang owner ay ang sariling bridge program, na naglalaman ng 1.001281 SOL na ipinapasa ng mga user. Ang wallet ng attacker ay may 2 SOL sa simula at walang anumang espesyal na karapatan.
Proyekto
Halaga / Paliwanag
Bridge Program
CJutNlr8d3oxdb11ReOLaZd5jPsqUxgwt2mv5e7equtE
Treasury account
DxGkzaMhP5Wy824GhoehErdD6MudfuEPGPwaV2s77FsM
Owner ng KuCoin
= Program-owned PDA
Initial balance ng KuCoin
1.001281 SOL (deposito ng user)
Initial Controller
0x0000...0000 (lahat ng zero, hindi itinakda)
Initial balance of attacker's wallet
2.000000 SOL
Mga kinakailangang pribilehiyo
WALANG KAILANGANG PAGPAPAWALA
Bilang ng mga transaksyon
2 na transaksyon
Final wallet balance
0.000000 SOL (na-clear)
Nakakuha ng kita ang attacker
+1.001281 SOL
Screenshot ng PoC (tests/poc-idl-hijack.ts):
Nakikita mo na, ang IdlCreateBuffer sa Step 1 ay nagtakda ng attacker bilang controller ng vault (hindi nagbago ang balanse); ang IdlCloseAccount sa Step 2 ay naglipat ng buong 1.001281 SOL ng vault patungo sa wallet ng attacker, at naging sero ang balanse ng vault.
Mga rekomendasyon para sa pagpapabuti/atubili:
(1) I-upgrade ang Anchor version: Ang pinakabagong bersyon ay nag-aayos ng problema na ito (ang IDL instruction ay magkakaroon ng malinaw na pagkakaiba sa IDL account at business account), at ang pinakadirektang at pinakamainam na paraan upang makaiwas ay ang pag-iwas sa paggamit ng mas lumang bersyon ng Anchor.
(2) I-enable ang no-idl sa pagbuo: I-disables ang pag-inject ng IDL instructions para sa mga program sa production environment, at alisin ang attack surface mula sa pinagmulan.
(3) Gamitin ang typed account kaysa sa裸 AccountInfo: Gamitin ang Account / SystemAccount at iba pang mga uri na may discriminator at owner verification para sa mga account ng pondo, upang maiwasan ang maling pagkilala ng IDL instruction.
(4) Minimizing the number of accounts that the program can empty: Magdagdag ng malinaw na pagkakakilanlan / seeds / discriminator sa PDA na naglalaman ng pera, at i-verify ang account discriminator sa mga mahahalagang utos.
Pangwakas
Ang susi sa vulnerability ay ang kombinasyon ng “framework na nagtatago ng utos + kulang sa pagkilala sa uri ng akawnt.” Dapat maunawaan ng mga developer na ang IDL command na automoang ikinukuwenta ng Anchor ay isang totoong panganib, at ang pag-upgrade sa bersyon ng framework at ang paggamit ng malakas na uri ng pagpapahiwatig sa mga akawnt ng pondo ay maaaring makapagpapawi nang epektibo ng ganitong uri ng panganib ng hindi pinahintulutang pagbawi ng pondo.
Ang Beosin ay isang pangunahing kompanya sa teknolohiya para sa seguridad at pagtutugma sa regulasyon ng blockchain, na nakatuon sa pag-audit ng seguridad ng smart contract bago ang paglunsad ng proyekto, pagmonito at pagpigil sa mga panganib sa seguridad habang nagpapatakbo ang proyekto, pagkuha muli ng mga nasakop, virtual asset anti-money laundering (AML), at pag-imbestiga at pagtatakas. Ang Beosin ay nagbigay ng “isang-solusyon” na mga produkto sa pagtutugma sa blockchain at serbisyo sa seguridad para sa 20+ na ahensya ng regulasyon at pagsasapilitan sa buong mundo, 200+ na mga serbisyong virtual asset, at 4,500+ na mga Web3 project. Mangyaring i-click ang box ng mensahe sa aming public account para makipag-ugnayan sa amin.

