मुख्य आशयावर जा

स्मार्ट कॉन्ट्रॅक्ट्सची चाचणी घेण्यासाठी Echidna चा वापर कसा करावा

Solidity
स्मार्ट कॉन्ट्रॅक्ट
सुरक्षा
चाचणी
fuzzing
प्रगत
Trailofbits
10 एप्रिल, 2020
12 मिनिट वाचन

इन्स्टॉलेशन

Echidna डॉकरद्वारे किंवा प्री-कंपाइल बायनरी वापरून इन्स्टॉल केले जाऊ शकते.

डॉकरद्वारे Echidna

docker pull trailofbits/eth-security-toolbox
docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox

शेवटचा कमांड तुमच्या सध्याच्या डिरेक्टरीमध्ये ऍक्सेस असलेल्या docker मध्ये eth-security-toolbox चालवतो. तुम्ही तुमच्या होस्टमधून फाइल्स बदलू शकता, आणि docker मधून फाइल्सवर टूल्स चालवू शकता

डॉकरमध्ये, चालवा :

solc-select 0.5.11
cd /home/training

बायनरी

https://github.com/crytic/echidna/releases/tag/v1.4.0.0 (opens in a new tab)

प्रॉपर्टी-आधारित फझिंगची ओळख

Echidna एक प्रॉपर्टी-आधारित फझर आहे, ज्याचे वर्णन आम्ही आमच्या मागील ब्लॉगपोस्टमध्ये केले आहे (1 (opens in a new tab), 2 (opens in a new tab), 3 (opens in a new tab)).

फझिंग

Fuzzing (opens in a new tab) हे सुरक्षा समुदायामध्ये एक सुप्रसिद्ध तंत्र आहे. यात प्रोग्राममधील बग शोधण्यासाठी कमी-अधिक प्रमाणात यादृच्छिक इनपुट तयार करणे समाविष्ट आहे. पारंपारिक सॉफ्टवेअरसाठी फझर्स (जसे की AFL (opens in a new tab) किंवा LibFuzzer (opens in a new tab)) बग शोधण्यासाठी कार्यक्षम साधने म्हणून ओळखले जातात.

केवळ यादृच्छिक इनपुट तयार करण्यापलीकडे, चांगले इनपुट तयार करण्यासाठी अनेक तंत्रे आणि धोरणे आहेत, ज्यात खालील गोष्टींचा समावेश आहे:

  • प्रत्येक अंमलबजावणीतून अभिप्राय मिळवा आणि त्याचा वापर करून पिढीला मार्गदर्शन करा. उदाहरणार्थ, जर नवीन तयार केलेल्या इनपुटमुळे नवीन मार्ग सापडला, तर त्याच्या जवळ नवीन इनपुट तयार करणे अर्थपूर्ण ठरू शकते.
  • रचनात्मक मर्यादांचा आदर करून इनपुट तयार करणे. उदाहरणार्थ, जर तुमच्या इनपुटमध्ये चेकसमसह हेडर असेल, तर फझरला चेकसम प्रमाणित करणारे इनपुट तयार करू देणे अर्थपूर्ण ठरेल.
  • नवीन इनपुट तयार करण्यासाठी ज्ञात इनपुट वापरणे: जर तुमच्याकडे वैध इनपुटच्या मोठ्या डेटासेटमध्ये प्रवेश असेल, तर तुमचा फझर सुरवातीपासून त्याची निर्मिती सुरू करण्याऐवजी त्यातून नवीन इनपुट तयार करू शकतो. यांना सहसा seeds म्हटले जाते.

प्रॉपर्टी-आधारित फझिंग

Echidna फझरच्या एका विशिष्ट कुटुंबाशी संबंधित आहे: QuickCheck (opens in a new tab) द्वारे मोठ्या प्रमाणावर प्रेरित प्रॉपर्टी-आधारित फझिंग. क्रॅश शोधण्याचा प्रयत्न करणाऱ्या क्लासिक फझरच्या विपरीत, Echidna वापरकर्ता-परिभाषित इनव्हेरियंट्स तोडण्याचा प्रयत्न करेल.

स्मार्ट कॉन्ट्रॅक्ट्समध्ये, इनव्हेरियंट्स हे Solidity फंक्शन्स असतात, जे कॉन्ट्रॅक्ट पोहोचू शकणाऱ्या कोणत्याही चुकीच्या किंवा अवैध स्थितीचे प्रतिनिधित्व करू शकतात, ज्यात खालील गोष्टींचा समावेश आहे:

  • चुकीचे प्रवेश नियंत्रण: आक्रमणकर्ता कॉन्ट्रॅक्टचा मालक बनला.
  • चुकीचे स्टेट मशीन: कॉन्ट्रॅक्ट थांबवलेले असताना टोकन हस्तांतरित केले जाऊ शकतात.
  • चुकीचे अंकगणित: वापरकर्ता त्याचे शिल्लक कमी करू शकतो आणि अमर्यादित विनामूल्य टोकन मिळवू शकतो.

Echidna सह प्रॉपर्टीची चाचणी करणे

आम्ही Echidna सह स्मार्ट कॉन्ट्रॅक्टची चाचणी कशी करायची ते पाहू. लक्ष्य खालील स्मार्ट कॉन्ट्रॅक्ट आहे token.sol (opens in a new tab):

आम्ही असे गृहीत धरू की या टोकनमध्ये खालील गुणधर्म असणे आवश्यक आहे:

  • कोणाकडेही जास्तीत जास्त 1000 टोकन असू शकतात
  • टोकन हस्तांतरित केले जाऊ शकत नाही (ते ERC20 टोकन नाही)

एक प्रॉपर्टी लिहा

Echidna प्रॉपर्टीज या Solidity फंक्शन्स आहेत. एका प्रॉपर्टीमध्ये हे असणे आवश्यक आहे:

  • कोणतेही आर्ग्युमेंट नसावे
  • यशस्वी झाल्यास true परत करा
  • त्याचे नाव echidna ने सुरू झाले पाहिजे

Echidna हे करेल:

  • प्रॉपर्टीची चाचणी घेण्यासाठी आपोआप अनियंत्रित व्यवहार तयार करा.
  • एखाद्या प्रॉपर्टीला false परत करण्यास किंवा त्रुटी निर्माण करण्यास कारणीभूत ठरलेल्या कोणत्याही व्यवहारांची तक्रार करा.
  • प्रॉपर्टी कॉल करताना साईड-इफेक्ट टाळा (म्हणजे, जर प्रॉपर्टी स्टेट व्हेरिएबल बदलत असेल, तर चाचणीनंतर ते टाकून दिले जाते)

खालील प्रॉपर्टी तपासते की कॉलरकडे 1000 पेक्षा जास्त टोकन नाहीत:

function echidna_balance_under_1000() public view returns(bool){
      return balances[msg.sender] <= 1000;
}

तुमच्या कॉन्ट्रॅक्टला तुमच्या प्रॉपर्टीजपासून वेगळे करण्यासाठी इनहेरिटन्स वापरा:

contract TestToken is Token{
      function echidna_balance_under_1000() public view returns(bool){
            return balances[msg.sender] <= 1000;
      }
  }

token.sol (opens in a new tab) प्रॉपर्टी लागू करते आणि टोकनमधून इनहेरिट करते.

एक कॉन्ट्रॅक्ट सुरू करा

Echidna ला आर्ग्युमेंटशिवाय constructor ची आवश्यकता आहे. जर तुमच्या कॉन्ट्रॅक्टला विशिष्ट इनिशिएलायझेशनची आवश्यकता असेल, तर तुम्हाला ते कन्स्ट्रक्टरमध्ये करावे लागेल.

Echidna मध्ये काही विशिष्ट ॲड्रेसेस आहेत:

  • 0x00a329c0648769A73afAc7F9381E08FB43dBEA72 जे कन्स्ट्रक्टरला कॉल करते.
  • 0x10000, 0x20000, आणि 0x00a329C0648769a73afAC7F9381e08fb43DBEA70 जे यादृच्छिकपणे इतर फंक्शन्सना कॉल करतात.

आमच्या सध्याच्या उदाहरणात आम्हाला कोणत्याही विशिष्ट इनिशिएलायझेशनची आवश्यकता नाही, परिणामी आमचा कन्स्ट्रक्टर रिकामा आहे.

Echidna चालवा

Echidna यासह लॉन्च केले जाते:

echidna-test contract.sol

जर contract.sol मध्ये अनेक कॉन्ट्रॅक्ट्स असतील, तर तुम्ही लक्ष्य निर्दिष्ट करू शकता:

echidna-test contract.sol --contract MyContract

सारांश: एका प्रॉपर्टीची चाचणी करणे

खालील आमच्या उदाहरणावरील Echidna च्या रनचा सारांश देते:

contract TestToken is Token{
    constructor() public {}
        function echidna_balance_under_1000() public view returns(bool){
          return balances[msg.sender] <= 1000;
        }
  }

Echidna ला आढळले की जर backdoor कॉल केला तर प्रॉपर्टीचे उल्लंघन होते.

फझिंग मोहिमेदरम्यान कॉल करण्यासाठी फंक्शन्स फिल्टर करणे

आम्ही फझ करण्यासाठी फंक्शन्स कसे फिल्टर करायचे ते पाहू. लक्ष्य खालील स्मार्ट कॉन्ट्रॅक्ट आहे:

हे लहान उदाहरण Echidna ला स्टेट व्हेरिएबल बदलण्यासाठी व्यवहारांचा एक विशिष्ट क्रम शोधण्यास भाग पाडते. हे फझरसाठी कठीण आहे (यासाठी Manticore (opens in a new tab) सारखे सिम्बॉलिक एक्झिक्युशन टूल वापरण्याची शिफारस केली जाते). हे तपासण्यासाठी आपण Echidna चालवू शकतो:

echidna-test multi.sol
...
echidna_state4: passed! 🎉
Seed: -3684648582249875403

फिल्टरिंग फंक्शन्स

Echidna ला या कॉन्ट्रॅक्टची चाचणी घेण्यासाठी योग्य क्रम शोधण्यात अडचण येत आहे कारण दोन रीसेट फंक्शन्स (reset1 आणि reset2) सर्व स्टेट व्हेरिएबल्स false वर सेट करतील. तथापि, आपण एक विशेष Echidna वैशिष्ट्य वापरू शकतो, एकतर रीसेट फंक्शनला ब्लॅकलिस्ट करण्यासाठी किंवा फक्त f, g, h आणि i फंक्शन्सना व्हाइटलिस्ट करण्यासाठी.

फंक्शन्स ब्लॅकलिस्ट करण्यासाठी, आम्ही ही कॉन्फिगरेशन फाइल वापरू शकतो:

filterBlacklist: true
filterFunctions: ["reset1", "reset2"]

फंक्शन्स फिल्टर करण्याचा दुसरा दृष्टिकोन म्हणजे व्हाइटलिस्ट केलेल्या फंक्शन्सची यादी करणे. ते करण्यासाठी, आम्ही ही कॉन्फिगरेशन फाइल वापरू शकतो:

filterBlacklist: false
filterFunctions: ["f", "g", "h", "i"]
  • filterBlacklist हे डीफॉल्टनुसार true असते.
  • फिल्टरिंग केवळ नावाने केले जाईल (पॅरामीटर्सशिवाय). जर तुमच्याकडे f() आणि f(uint256) असेल, तर "f" फिल्टर दोन्ही फंक्शन्सना जुळेल.

Echidna चालवा

blacklist.yaml कॉन्फिगरेशन फाइलसह Echidna चालवण्यासाठी:

echidna-test multi.sol --config blacklist.yaml
...
echidna_state4: failed!💥
  Call sequence:
    f(12)
    g(8)
    h(42)
    i()

Echidna जवळजवळ त्वरित प्रॉपर्टीला खोटे ठरवण्यासाठी व्यवहारांचा क्रम शोधेल.

सारांश: फिल्टरिंग फंक्शन्स

Echidna फझिंग मोहिमेदरम्यान कॉल करण्यासाठी फंक्शन्स ब्लॅकलिस्ट किंवा व्हाइटलिस्ट करू शकते:

filterBlacklist: true
filterFunctions: ["f1", "f2", "f3"]
echidna-test contract.sol --config config.yaml
...

filterBlacklist बुलियनच्या मूल्यांनुसार Echidna f1, f2 आणि f3 ला ब्लॅकलिस्ट करून किंवा फक्त त्यांना कॉल करून फझिंग मोहीम सुरू करते.

Echidna सह Solidity's assert ची चाचणी कशी करावी

या लहान ट्युटोरियलमध्ये, आम्ही कॉन्ट्रॅक्ट्समध्ये अॅसर्शन चेकिंगची चाचणी घेण्यासाठी Echidna चा वापर कसा करायचा हे दाखवणार आहोत. समजा आपल्याकडे यासारखा एक कॉन्ट्रॅक्ट आहे:

एक अॅसर्शन लिहा

त्यातील फरक परत केल्यानंतर tmp हे counter पेक्षा कमी किंवा समान आहे याची आम्ही खात्री करू इच्छितो. आपण Echidna प्रॉपर्टी लिहू शकतो, पण आपल्याला tmp मूल्य कुठेतरी साठवावे लागेल. त्याऐवजी, आपण यासारखे अॅसर्शन वापरू शकतो:

Echidna चालवा

अॅसर्शन अयशस्वी चाचणी सक्षम करण्यासाठी, एक Echidna कॉन्फिगरेशन फाइल (opens in a new tab) config.yaml तयार करा:

checkAsserts: true

जेव्हा आपण Echidna मध्ये हा कॉन्ट्रॅक्ट चालवतो, तेव्हा आपल्याला अपेक्षित परिणाम मिळतात:

तुम्ही बघू शकता, Echidna inc फंक्शनमध्ये काही अॅसर्शन अयशस्वी झाल्याची तक्रार करते. प्रत्येक फंक्शनमध्ये एकापेक्षा जास्त अॅसर्शन जोडणे शक्य आहे, परंतु कोणते अॅसर्शन अयशस्वी झाले हे Echidna सांगू शकत नाही.

अॅसर्शन्स केव्हा आणि कसे वापरावे

अॅसर्शन्स स्पष्ट प्रॉपर्टीजसाठी पर्याय म्हणून वापरले जाऊ शकतात, विशेषतः जर तपासण्याची परिस्थिती थेट काही ऑपरेशन f च्या योग्य वापराशी संबंधित असेल. काही कोडनंतर अॅसर्शन्स जोडल्याने हे सुनिश्चित होईल की तपासणी त्याच्या अंमलबजावणीनंतर लगेच होईल:

function f(..) public {
    // काही जटिल कोड
    ...
    assert (condition);
    ...
}

याउलट, स्पष्ट Echidna प्रॉपर्टी वापरल्याने व्यवहार यादृच्छिकपणे अंमलात येतील आणि ते केव्हा तपासले जाईल हे लागू करण्याचा कोणताही सोपा मार्ग नाही. हा वर्कअराउंड करणे अजूनही शक्य आहे:

function echidna_assert_after_f() public returns (bool) {
    f(..);
    return(condition);
}

तथापि, काही समस्या आहेत:

  • जर f हे internal किंवा external म्हणून घोषित केले असेल तर ते अयशस्वी होते.
  • f ला कॉल करण्यासाठी कोणते आर्ग्युमेंट्स वापरावे हे अस्पष्ट आहे.
  • जर f रिव्हर्ट झाले, तर प्रॉपर्टी अयशस्वी होईल.

सर्वसाधारणपणे, आम्ही अॅसर्शन्स कसे वापरावे यावर जॉन रेगेहर यांच्या शिफारसीचे (opens in a new tab) पालन करण्याची शिफारस करतो:

  • अॅसर्शन तपासणी दरम्यान कोणताही साईड इफेक्ट जबरदस्तीने करू नका. उदाहरणार्थ: assert(ChangeStateAndReturn() == 1)
  • स्पष्ट विधाने निश्चित करू नका. उदाहरणार्थ, assert(var >= 0) जेथे var हे uint म्हणून घोषित केले आहे.

शेवटी, कृपया assert ऐवजी require वापरू नका, कारण Echidna ते शोधू शकणार नाही (परंतु कॉन्ट्रॅक्ट तरीही रिव्हर्ट होईल).

सारांश: अॅसर्शन चेकिंग

खालील आमच्या उदाहरणावरील Echidna च्या रनचा सारांश देते:

Echidna ला आढळले की inc मधील अॅसर्शन अयशस्वी होऊ शकते जर हे फंक्शन मोठ्या आर्ग्युमेंट्ससह अनेक वेळा कॉल केले गेले.

Echidna कॉर्पस गोळा करणे आणि त्यात बदल करणे

आम्ही Echidna सह व्यवहारांचा कॉर्पस कसा गोळा करायचा आणि वापरायचा ते पाहू. लक्ष्य खालील स्मार्ट कॉन्ट्रॅक्ट आहे magic.sol (opens in a new tab):

हे लहान उदाहरण Echidna ला स्टेट व्हेरिएबल बदलण्यासाठी काही विशिष्ट मूल्ये शोधण्यास भाग पाडते. हे फझरसाठी कठीण आहे (यासाठी Manticore (opens in a new tab) सारखे सिम्बॉलिक एक्झिक्युशन टूल वापरण्याची शिफारस केली जाते). हे तपासण्यासाठी आपण Echidna चालवू शकतो:

echidna-test magic.sol
...

echidna_magic_values: passed! 🎉

Seed: 2221503356319272685

तथापि, ही फझिंग मोहीम चालवताना कॉर्पस गोळा करण्यासाठी आम्ही अजूनही Echidna वापरू शकतो.

कॉर्पस गोळा करणे

कॉर्पस संकलन सक्षम करण्यासाठी, एक कॉर्पस डिरेक्टरी तयार करा:

mkdir corpus-magic

आणि एक Echidna कॉन्फिगरेशन फाइल (opens in a new tab) config.yaml:

coverage: true
corpusDir: "corpus-magic"

आता आपण आपले टूल चालवू शकतो आणि गोळा केलेला कॉर्पस तपासू शकतो:

echidna-test magic.sol --config config.yaml

Echidna अजूनही योग्य मॅजिक व्हॅल्यूज शोधू शकत नाही, परंतु आपण त्याने गोळा केलेल्या कॉर्पसवर एक नजर टाकू शकतो. उदाहरणार्थ, यापैकी एक फाइल होती:

स्पष्टपणे, हे इनपुट आमच्या प्रॉपर्टीमध्ये अपयश आणणार नाही. तथापि, पुढील चरणात, आम्ही त्यासाठी त्यात कसे बदल करायचे ते पाहू.

कॉर्पस सीडिंग करणे

magic फंक्शन हाताळण्यासाठी Echidna ला काही मदतीची गरज आहे. आम्ही इनपुट कॉपी करणार आहोत आणि त्यासाठी योग्य पॅरामीटर्स वापरण्यासाठी त्यात बदल करणार आहोत:

cp corpus/2712688662897926208.txt corpus/new.txt

आम्ही magic(42,129,333,0) ला कॉल करण्यासाठी new.txt मध्ये बदल करू. आता, आपण Echidna पुन्हा चालवू शकतो:

यावेळी, त्याला आढळले की प्रॉपर्टीचे त्वरित उल्लंघन झाले आहे.

उच्च गॅस वापरासह व्यवहार शोधणे

आम्ही Echidna सह उच्च गॅस वापरासह व्यवहार कसे शोधायचे ते पाहू. लक्ष्य खालील स्मार्ट कॉन्ट्रॅक्ट आहे:

येथे expensive मध्ये मोठा गॅस वापर असू शकतो.

सध्या, Echidna ला नेहमी चाचणीसाठी एक प्रॉपर्टीची आवश्यकता असते: येथे echidna_test नेहमी true परत करते. हे तपासण्यासाठी आपण Echidna चालवू शकतो:

echidna-test gas.sol
...
echidna_test: passed! 🎉

Seed: 2320549945714142710

गॅस वापर मोजणे

Echidna सह गॅस वापर सक्षम करण्यासाठी, एक कॉन्फिगरेशन फाइल config.yaml तयार करा:

estimateGas: true

या उदाहरणात, आम्ही परिणाम समजण्यास सोपे करण्यासाठी व्यवहार क्रमाचा आकार देखील कमी करू:

seqLen: 2
estimateGas: true

Echidna चालवा

एकदा आम्ही कॉन्फिगरेशन फाइल तयार केल्यावर, आम्ही Echidna याप्रमाणे चालवू शकतो:

  • दाखवलेला गॅस हा HEVM (opens in a new tab) द्वारे प्रदान केलेला एक अंदाज आहे.

गॅस-कमी करणारे कॉल्स फिल्टर करणे

वरील फझिंग मोहिमेदरम्यान कॉल करण्यासाठी फंक्शन्स फिल्टर करणे यावरील ट्युटोरियल तुमच्या चाचणीमधून काही फंक्शन्स कसे काढायचे हे दाखवते.
अचूक गॅस अंदाज मिळवण्यासाठी हे महत्त्वपूर्ण असू शकते. खालील उदाहरण विचारात घ्या:

जर Echidna सर्व फंक्शन्सना कॉल करू शकत असेल, तर त्याला उच्च गॅस खर्चासह व्यवहार सहज सापडणार नाहीत:

कारण खर्च addrs च्या आकारावर अवलंबून असतो आणि यादृच्छिक कॉल्समुळे ॲरे जवळजवळ रिकामा राहतो. तथापि, pop आणि clear ला ब्लॅकलिस्ट केल्याने आम्हाला बरेच चांगले परिणाम मिळतात:

filterBlacklist: true
filterFunctions: ["pop", "clear"]
echidna-test pushpop.sol --config config.yaml
...
push used a maximum of 40839 gas
...
check used a maximum of 1484472 gas

सारांश: उच्च गॅस वापरासह व्यवहार शोधणे

estimateGas कॉन्फिगरेशन पर्याय वापरून Echidna उच्च गॅस वापरासह व्यवहार शोधू शकते:

estimateGas: true
echidna-test contract.sol --config config.yaml
...

एकदा फझिंग मोहीम संपली की, Echidna प्रत्येक फंक्शनसाठी कमाल गॅस वापरासह एक क्रम नोंदवेल.

पृष्ठ शेवटचे अपडेट: 3 मार्च, 2026

हे ट्युटोरियल उपयुक्त होते का?