Anchor 是 Solana इकोसिस्टम में सबसे प्रमुख डेवलपमेंट फ्रेमवर्क है। यह डिक्लेरेटिव अकाउंट वेरिफिकेशन, ऑटोमैटिक सीरियलाइजेशन और बिल्ट-इन सुरक्षा चेक जैसी सुविधाओं के माध्यम से डेवलपमेंट की बाधाओं को काफी कम करता है। हालाँकि, फ्रेमवर्क आसानी प्रदान करते समय, पीछे की ओर प्रत्येक प्रोग्राम में कुछ ऐसे आंतरिक निर्देश डालता है जिनके बारे में डेवलपर्स को जानकारी नहीं होती। ये “छिपे” निर्देश विशिष्ट परिस्थितियों में हमलावरों द्वारा दुरुपयोग किए जा सकते हैं, जिससे गंभीर धन का नुकसान हो सकता है।
Beosin सुरक्षा टीम इस लेख में एक महत्वपूर्ण दुर्बलता पैटर्न को उजागर करेगी: निम्न संस्करण के Anchor में, जब डेवलपर AccountInfo का उपयोग प्रोग्राम स्वामित्व वाले PDA खाते को घोषित करने के लिए करते हैं, तो हमलावर Anchor द्वारा स्वचालित रूप से इंजेक्ट किए गए IDL निर्देश का उपयोग करके केवल दो चरणों में खाते का नियंत्रण हासिल कर सकते हैं और उसमें मौजूद सभी SOL को खाली कर सकते हैं, जिसके लिए किसी विशेषाधिकार की आवश्यकता नहीं होती।
एक, IDL निर्देश और संबंधित तंत्र का विश्लेषण
1.1 IDL निर्देश
एंकर डिफ़ॉल्ट रूप से प्रत्येक प्रोग्राम में एक सेट आईडीएल (इंटरफ़ेस डिफ़िनिशन लैंग्वेज, इंटरफ़ेस डिफ़िनिशन भाषा) प्रबंधन निर्देश डालता है, जब तक कि निर्माण के दौरान no-idl फीचर को स्पष्ट रूप से सक्षम न किया जाए। इन निर्देशों में शामिल हैं:
IdlCreateAccount: एक ऑन-चेन IDL खाता बनाएं
IdlWrite: आईडीएल खाते / कैश खाते में डेटा लिखें
IdlSetAuthority: IDL खाते के अधिकारी (नियंत्रक) को बदलें
IdlCloseAccount: IDL खाता बंद करें और इसके सभी lamports को निर्दिष्ट प्राप्तकर्ता को स्थानांतरित करें
IdlResizeAccount: IDL खाते का आकार बदलें
IdlCreateBuffer: IDL बफर खाता (IDL Buffer) बनाएं
IdlSetBuffer: बफर अकाउंट के डेटा से ऑफिशियल IDL अकाउंट को ओवरराइट करें
ये निर्देश ऑन-चेन IDL प्रबंधन के लिए डिज़ाइन किए गए थे, लेकिन इनकी प्रोग्राम द्वारा स्वामित्व वाले खातों पर विशेष कार्रवाई की क्षमता होती है (डेटा पढ़ना/लिखना, नियंत्रक बदलना, बंद करना और lamports हस्तांतरित करना) — यही सुरक्षा दुरुपयोग का मूल कारण है। हमलावर को विकासक द्वारा लिखी गई कोई भी व्यावसायिक निर्देश नहीं कॉल करना पड़ता, बल्कि वे सीधे इन अंतर्निहित निर्देशों को कॉल कर सकते हैं।
1.2 IDL बफर खाता
IDL बफर खाता (IDL Buffer) Anchor द्वारा बड़े IDL डेटा को टुकड़ों में अपलोड करने के लिए पेश किया गया अस्थायी खाता है। चूंकि पूर्ण IDL (JSON संपीड़ित) एकल लेनदेन की आकार सीमा को पार कर सकता है, Anchor अनुमति देता है कि पहले IdlCreateBuffer के माध्यम से एक बफर बनाया जाए, फिर कई IdlWrite लेनदेनों के माध्यम से बैच में लिखा जाए, और अंत में IdlSetBuffer के माध्यम से एक ही बार में सबमिट किया जाए।
मुख्य बिंदु IDL खाता / बफर खाते की डेटा संरचना है: यह एक निश्चित लेआउट वाले हेडर के साथ शुरू होता है, जिसमें authority: Pubkey क्षेत्र होता है (इस लेख में परीक्षण आउटपुट में controller कहा गया है)। IDL निर्देश इस क्षेत्र के माध्यम से यह निर्धारित करते हैं कि "कौन इस खाते को संचालित करने का अधिकार रखता है"।
लेकिन समस्या यह है: निम्न संस्करण के Anchor की IdlCreateBuffer प्रक्रिया, जो इस प्रोग्राम के स्वामित्व वाले किसी भी खाते को बफर खाते के रूप में प्रारंभ करती है और authority को सीधे लेन-देन हस्ताक्षरकर्ता के रूप में सेट कर देती है। अर्थात, जब तक एक खाते का owner इस प्रोग्राम है (उदाहरण के लिए, प्रोग्राम का PDA भंडार), हमलावर इसका controller बन सकता है, और फिर IdlCloseAccount का उपयोग करके खाते से सभी SOL को वैध रूप से हटा सकता है।
1.3 दुर्भावनापूर्ण निष्पादन की शर्तें
दरार को ट्रिगर करने के लिए निम्नलिखित सभी शर्तों को एक साथ पूरा करना आवश्यक है:
- पुराने संस्करण के Anchor का उपयोग करें: खतरनाक IDL निर्देश में पर्याप्त खाता जांच नहीं है, जिससे व्यावसायिक खाते को IDL खाते के रूप में गलती से संचालित किया जा सकता है।
- no-idl अक्षम है: प्रोग्राम बिल्ड के दौरान डिफ़ॉल्ट इंजेक्टेड IDL इंस्ट्रक्शन एंट्री को बरकरार रखता है, जिससे अटैक सरफेस बाहर की ओर खुली होती है।
- AccountInfo के साथ प्रोग्राम द्वारा स्वीकृत PDA को घोषित करें: डेवलपर वैध खाते (जैसे ट्रेजरी PDA) को नंगे AccountInfo के साथ ले जाते हैं, जिसमें Anchor प्रकार-संबद्ध खाते (जैसे Account) द्वारा स्वयं प्रदान किए गए discriminator / owner पहचानकर्ता नहीं होते, जिससे IDL निर्देश के लिए यह खाता IDL खाते से “अविभेद्य” लगता है।
- खाता प्रोग्राम के स्वामित्व में है और lamports रखता है: owner == यह प्रोग्राम ही IDL निर्देश के लिए इसे संचालित करने की पूर्वशर्त है; SOL रखना ही इसे खाली करने का मूल्य बनाता है।
ऊपर दिए गए शर्तों को पूरा करने के बाद, हमलावर को केवल दो सामान्य लेन-देन की आवश्यकता होती है: पहले IdlCreateBuffer के माध्यम से controller को हड़प लें, फिर IdlCloseAccount के माध्यम से सभी SOL ले जाएं, और इस पूरी प्रक्रिया में लक्ष्य प्रोग्राम को कोई अनुमति देने की आवश्यकता नहीं होती।
1.4 अटैक चेन
नीचे एंकर के आंतरिक कार्यान्वयन के साथ, हम धीरे-धीरे विश्लेषण करेंगे कि हमलावर कैसे केवल दो अंतर्निहित निर्देशों का उपयोग करके एक सामान्य फंड वॉल्ट को "छलकार" बनाकर उसे खाली कर देता है।
अटैक प्रक्रिया का अवलोकन
चरण 1: IdlCreateBuffer(treasury, signer = attacker)
└─ treasury.controller ==> attacker (नियंत्रक बन जाए)
चरण 2: IdlCloseAccount(treasury, authority = attacker, dest = attacker)
└─ treasury.lamports ==> attacker (ख казन को खाली कर दें)
पहला चरण: IdlCreateBuffer द्वारा अधिकार लेना
Anchor का आंतरिक कार्यान्वयन लगभग इस प्रकार है:
#[derive(Accounts)]pub struct IdlCreateBuffer {
#[account(zero)] // ← Key: एक खाता जिसका डिस्क्रिमिनेटर सभी 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; // set attack as authority Ok(())}
#[account(zero)] का अर्थ है: एक ऐसा खाता, जिसका discriminator पूरी तरह से शून्य है और जिसे प्रोग्राम के स्वामित्व में है, उसे अप्रारंभित IDL खाते के रूप में प्रारंभ करें। और vault ठीक इन दोनों शर्तों को संतुष्ट करता है:
वॉल्ट की स्थिति
शर्तें
प्रोग्राम द्वारा स्वामित्व में
init_if_needed के बाद owner को इस प्रोग्राम के रूप में सेट किया गया है
discriminator सभी शून्य
AccountInfo प्रकार का उपयोग करते हुए, Anchor discriminator नहीं लिखता है, डेटा सभी शून्य हैं
इसलिए हमलावर ने vault को IdlCreateBuffer में पास कर दिया:
- Anchor, IdlAccount के discriminator को vault से पहले 8 बाइट्स में लिखता है;
- आक्रमणकारी की सार्वजनिक कुंजी को authority क्षेत्र में लिखें।
इस प्रकार, वॉल्ट को एक ऐसे IdlAccount के रूप में "छल" कर दिया गया है जिसका अधिकारी हमलावर है—और इसके अंदर की SOL की कोई राशि नहीं बदली गई है।
दूसरा कदम: IdlCloseAccount —— फंड्स क्लियर करें
#[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}
इस समय वॉल्ट पूरी तरह से सत्यापित हो चुका है:
- discriminator इडल अकाउंट से मेल खाता है
- authority फील्ड = हमलावर की सार्वजनिक कुंजी
- has_one = authority जांच सफल
इसलिए वॉल्ट के भीतर सभी लैमपोर्ट्स (उपयोगकर्ता द्वारा जमा किए गए सभी SOL) कानूनी रूप से हमलावर के खाते में स्थानांतरित कर दिए गए, जिससे वॉल्ट शून्य हो गया।
Root cause:
deposit.rs में प्रकारयुक्त Anchor खाते के बजाय AccountInfo का उपयोग करना इस दुर्भावना का घातक मूल कारण है:
(1) प्रकार के खाते (जैसे Account) को प्रारंभ करते समय इस संरचना के लिए विशिष्ट 8 बाइट डिस्क्रिमिनेटर लिखा जाता है;
(2) अपना discriminator लिखने के बाद, Anchor इसे IdlAccount के रूप में नहीं मान सकता (discriminator मेल नहीं खाता), इसलिए पहले चरण का IdlCreateBuffer विफल हो जाएगा, और हमले की श्रृंखला टूट जाएगी।
द्वितीय: मामला अध्ययन
यह परीक्षण Anchor 0.31.0 पर आधारित क्रॉस-चेन ब्रिज कॉन्ट्रैक्ट PoC के आधार पर किया गया है। परीक्षण एक वास्तविक ब्रिज खजाने (Bridge Treasury) का अनुकरण करता है, जिसका मालिक ब्रिज प्रोग्राम है और जिसमें उपयोगकर्ता द्वारा जमा किए गए 1.001281 SOL हैं। हमलावर की वॉलेट में शुरुआत में 2 SOL हैं और कोई विशेषाधिकार नहीं है।
प्रोजेक्ट
मान / विवरण
ब्रिज प्रोग्राम
CJutNlr8d3oxdb11ReOLaZd5jPsqUxgwt2mv5e7equtE
ट्रेजरी खाता
DxGkzaMhP5Wy824GhoehErdD6MudfuEPGPwaV2s77FsM
कुकॉइन मालिक
= ब्रिज प्रोग्राम (प्रोग्राम-ओन्ड पीडीए)
KuCoin प्रारंभिक शेष
1.001281 SOL (उपयोगकर्ता द्वारा निवेश की गई राशि)
प्रारंभिक Controller
0x0000...0000 (सभी शून्य, सेट नहीं किया गया)
Attacker wallet initial balance
2.000000 SOL
आवश्यक अधिकार
कोई अनुमति नहीं
ट्रेडिंग ट्रांजैक्शन
2 लेनदेन
KuCoin का अंतिम शेष
0.000000 SOL (खाली कर दिया गया)
Attacker profits
+1.001281 SOL
PoC रन स्क्रीनशॉट (tests/poc-idl-hijack.ts):
देखा जा सकता है कि Step 1 का IdlCreateBuffer आक्रमणकारी को खजाने का कंट्रोलर बना देता है (शेष बरकरार रहता है); Step 2 का IdlCloseAccount खजाने की 1.001281 SOL की सम्पूर्ण राशि आक्रमणकारी की वॉलेट में स्थानांतरित कर देता है, जिससे खजाने का शेष शून्य हो जाता है।
सुधार/सुरक्षा सुझाव:
(1) एंकर संस्करण को अपग्रेड करें: नवीनतम संस्करण इस समस्या को ठीक कर दिया गया है (IDL निर्देश IDL खातों और व्यावसायिक खातों के बीच कठोरता से भेद करते हैं), और निम्न संस्करण के एंकर का उपयोग न करना सबसे सीधा और सबसे मूलभूत सुरक्षा उपाय है।
(2) बिल्ड के दौरान no-idl सक्षम करें: उत्पादन पर्यावरण के लिए IDL निर्देश इंजेक्शन को स्पष्ट रूप से अक्षम करें, ताकि इस हमले के स्रोत को समाप्त किया जा सके।
(3) खाली AccountInfo के बजाय टाइपड खाते का उपयोग करें: वित्तीय खाते को Account / SystemAccount जैसे discriminator और owner जांच वाले प्रकारों द्वारा वहन करें, ताकि इसे IDL निर्देश गलती से पहचान न सके।
(4) न्यूनतमीकृत प्रोग्राम द्वारा स्वामित्व वाले खाते: धन भंडारण के लिए PDA पर स्पष्ट owner / seeds / discriminator सीमाएँ जोड़ें और महत्वपूर्ण निर्देशों में खाता विभेदक की जाँच करें।
निष्कर्ष
इस वैधता की मूल बात “फ्रेमवर्क छिपाई गई निर्देश + खाता प्रकार की पहचान का अभाव” का संयोजन है। विकासकों को यह समझना चाहिए कि Anchor द्वारा डिफ़ॉल्ट रूप से इंजेक्ट किए गए IDL निर्देश वास्तविक हमले के क्षेत्र हैं, और फ्रेमवर्क संस्करण को अपग्रेड करने और धन खातों पर मजबूत प्रकार सीमाएँ लागू करने से इस प्रकार के अनधिकृत धन खाली करने के जोखिम को प्रभावी ढंग से दूर किया जा सकता है।
Beosin एक अग्रणी ब्लॉकचेन सुरक्षा और नियामक अनुपालन टेक कंपनी है, जो प्रोजेक्ट लॉन्च से पहले स्मार्ट कॉन्ट्रैक्ट सुरक्षा ऑडिट, प्रोजेक्ट चलाने के दौरान सुरक्षा जोखिम की निगरानी और रोकथाम, चोरी हुए संपत्ति की वापसी, वर्चुअल एसेट धोखाधड़ी के खिलाफ प्रतिबंध (AML) और जांच और ट्रैकिंग पर केंद्रित है। Beosin ने वैश्विक स्तर पर 20 से अधिक देशों और क्षेत्रों के नियामक और कानून प्रवर्तन एजेंसियों, 200 से अधिक वर्चुअल एसेट सेवा प्रदाताओं और 4500 से अधिक Web3 प्रोजेक्ट्स को “वन-स्टॉप” ब्लॉकचेन कम्प्लायंस प्रोडक्ट + सुरक्षा सेवाएं प्रदान की हैं। कृपया हमसे संपर्क करने के लिए हमारे गूगल प्रोफाइल में संदेश भेजें।

