प्रमुख मजकुराकडे जा

स्मार्ट कॉन्ट्रॅक्ट्समधील बग शोधण्यासाठी Manticore चा वापर कसा करायचा

सॉलिडिटी
स्मार्ट कॉन्ट्रॅक्ट
सुरक्षा
चाचणी
औपचारिक पडताळणी
प्रगत
Trailofbits
१३ जानेवारी, २०२०
11 मिनिट वाचन

स्मार्ट कॉन्ट्रॅक्ट्समधील बग आपोआप शोधण्यासाठी Manticore चा वापर कसा करायचा हे दाखवणे या ट्युटोरियलचे उद्दिष्ट आहे.

इन्स्टॉलेशन

Manticore साठी >= python 3.6 आवश्यक आहे. हे pip द्वारे किंवा docker वापरून इंस्टॉल केले जाऊ शकते.

docker द्वारे Manticore

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

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

docker मध्ये, चालवा:

solc-select 0.5.11
cd /home/trufflecon/

pip द्वारे Manticore

pip3 install --user manticore

solc 0.5.11 ची शिफारस केली जाते.

स्क्रिप्ट चालवणे

python 3 सह python स्क्रिप्ट चालवण्यासाठी:

python3 script.py

डायनॅमिक सिम्बॉलिक एक्झिक्युशनची ओळख

थोडक्यात डायनॅमिक सिम्बॉलिक एक्झिक्युशन

डायनॅमिक सिम्बॉलिक एक्झिक्युशन (DSE) हे एक प्रोग्राम विश्लेषण तंत्र आहे जे उच्च स्तरीय सिमेंटिक जागरुकतेसह स्टेट स्पेस एक्सप्लोर करते. हे तंत्र "प्रोग्राम पाथ्स" च्या शोधावर आधारित आहे, जे path predicates नावाच्या गणितीय सूत्रांद्वारे दर्शविले जातात. संकल्पनात्मकदृष्ट्या, हे तंत्र दोन टप्प्यांमध्ये पाथ प्रेडिकेट्सवर कार्य करते:

  1. ते प्रोग्रामच्या इनपुटवरील मर्यादा वापरून तयार केले जातात.
  2. त्यांचा वापर प्रोग्राम इनपुट तयार करण्यासाठी केला जातो ज्यामुळे संबंधित पाथ्स कार्यान्वित होतील.

या दृष्टिकोनामुळे कोणतेही फॉल्स पॉझिटिव्ह तयार होत नाहीत कारण सर्व ओळखलेले प्रोग्राम स्टेट्स ठोस अंमलबजावणी दरम्यान ट्रिगर केले जाऊ शकतात. उदाहरणार्थ, जर विश्लेषणात एखादा इंटीजर ओव्हरफ्लो आढळला, तर तो पुनरुत्पादित होण्याची हमी आहे.

पाथ प्रेडिकेट उदाहरण

DSE कसे कार्य करते याची कल्पना येण्यासाठी, खालील उदाहरण विचारात घ्या:

1function f(uint a){
2
3 if (a == 65) {
4 // एक बग आहे
5 }
6
7}

f() मध्ये दोन पाथ असल्याने, DSE दोन भिन्न पाथ प्रेडिकेट्स तयार करेल:

  • पाथ 1: a == 65
  • पाथ 2: Not (a == 65)

प्रत्येक पाथ प्रेडिकेट हे एक गणितीय सूत्र आहे जे SMT solveropens in a new tab नावाच्या सॉल्व्हरला दिले जाऊ शकते, जो समीकरण सोडवण्याचा प्रयत्न करेल. पाथ 1 साठी, सॉल्व्हर सांगेल की पाथ a = 65 सह एक्सप्लोर केला जाऊ शकतो. पाथ 2 साठी, सॉल्व्हर a ला 65 व्यतिरिक्त कोणतेही मूल्य देऊ शकतो, उदाहरणार्थ a = 0.

गुणधर्मांची पडताळणी करणे

Manticore प्रत्येक पाथच्या सर्व एक्झिक्युशनवर पूर्ण नियंत्रण ठेवण्यास परवानगी देतो. परिणामी, हे तुम्हाला जवळजवळ कोणत्याही गोष्टीवर अनियंत्रित मर्यादा घालण्याची परवानगी देते. हे नियंत्रण कॉन्ट्रॅक्टवर गुणधर्म तयार करण्यास अनुमती देते.

खालील उदाहरण विचारात घ्या:

1function unsafe_add(uint a, uint b) returns(uint c){
2 c = a + b; // ओव्हरफ्लो संरक्षण नाही
3 return c;
4}

येथे फंक्शनमध्ये एक्सप्लोर करण्यासाठी फक्त एकच पाथ आहे:

  • पाथ 1: c = a + b

Manticore वापरून, तुम्ही ओव्हरफ्लो तपासू शकता, आणि पाथ प्रेडिकेटमध्ये मर्यादा घालू शकता:

  • c = a + b AND (c < a OR c < b)

जर a आणि b साठी असे मूल्यमापन शोधणे शक्य असेल ज्यासाठी वरील पाथ प्रेडिकेट व्यवहार्य आहे, तर याचा अर्थ तुम्हाला ओव्हरफ्लो आढळला आहे. उदाहरणार्थ सॉल्व्हर a = 10 , b = MAXUINT256 हे इनपुट तयार करू शकतो.

तुम्ही एक निश्चित आवृत्ती विचारात घेतल्यास:

1function safe_add(uint a, uint b) returns(uint c){
2 c = a + b;
3 require(c>=a);
4 require(c>=b);
5 return c;
6}

ओव्हरफ्लो तपासणीसह संबंधित सूत्र असेल:

  • c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)

हे सूत्र सोडवले जाऊ शकत नाही; दुसऱ्या शब्दांत, हा एक पुरावा आहे की safe_add मध्ये, c नेहमी वाढेल.

त्यामुळे DSE हे एक शक्तिशाली साधन आहे, जे तुमच्या कोडवर अनियंत्रित मर्यादांची पडताळणी करू शकते.

Manticore अंतर्गत चालवणे

Manticore API सह स्मार्ट कॉन्ट्रॅक्ट कसे एक्सप्लोर करायचे ते आपण पाहू. लक्ष्य खालील स्मार्ट कॉन्ट्रॅक्ट example.solopens in a new tab आहे:

1pragma solidity >=0.4.24 <0.6.0;
2
3contract Simple {
4 function f(uint a) payable public{
5 if (a == 65) {
6 revert();
7 }
8 }
9}
सर्व दाखवा

स्वतंत्र एक्सप्लोरेशन चालवा

तुम्ही खालील कमांडद्वारे थेट स्मार्ट कॉन्ट्रॅक्टवर Manticore चालवू शकता (project एक Solidity फाइल, किंवा प्रोजेक्ट डिरेक्टरी असू शकते):

$ manticore project

तुम्हाला यासारख्या टेस्टकेसचे आउटपुट मिळेल (क्रम बदलू शकतो):

1...
2... m.c.manticore:INFO: Generated testcase No. 0 - STOP
3... m.c.manticore:INFO: Generated testcase No. 1 - REVERT
4... m.c.manticore:INFO: Generated testcase No. 2 - RETURN
5... m.c.manticore:INFO: Generated testcase No. 3 - REVERT
6... m.c.manticore:INFO: Generated testcase No. 4 - STOP
7... m.c.manticore:INFO: Generated testcase No. 5 - REVERT
8... m.c.manticore:INFO: Generated testcase No. 6 - REVERT
9... m.c.manticore:INFO: Results in /home/ethsec/workshops/Automated Smart Contracts Audit - TruffleCon 2018/manticore/examples/mcore_t6vi6ij3
10...
सर्व दाखवा

अतिरिक्त माहितीशिवाय, Manticore नवीन सिम्बॉलिक ट्रान्झॅक्शनसह कॉन्ट्रॅक्ट एक्सप्लोर करेल जोपर्यंत तो कॉन्ट्रॅक्टवर नवीन पाथ्स एक्सप्लोर करत नाही. Manticore अयशस्वी ट्रान्झॅक्शननंतर (उदा: रिव्हर्टनंतर) नवीन ट्रान्झॅक्शन चालवत नाही.

Manticore mcore_* डिरेक्टरीमध्ये माहिती आउटपुट करेल. इतर गोष्टींबरोबरच, तुम्हाला या डिरेक्टरीमध्ये आढळेल:

  • global.summary: कव्हरेज आणि कंपाइलर चेतावणी
  • test_XXXXX.summary: कव्हरेज, शेवटची सूचना, प्रति टेस्ट केस अकाउंट बॅलन्स
  • test_XXXXX.tx: प्रति टेस्ट केस ट्रान्झॅक्शनची तपशीलवार यादी

येथे Manticore ला 7 टेस्ट केसेस आढळल्या, ज्या खालीलप्रमाणे आहेत (फाईल नावाचा क्रम बदलू शकतो):

ट्रान्झॅक्शन 0ट्रान्झॅक्शन 1ट्रान्झॅक्शन 2परिणाम
test_00000000.txकॉन्ट्रॅक्ट निर्मितीf(!=65)f(!=65)STOP
test_00000001.txकॉन्ट्रॅक्ट निर्मितीफॉलबॅक फंक्शनREVERT
test_00000002.txकॉन्ट्रॅक्ट निर्मितीRETURN
test_00000003.txकॉन्ट्रॅक्ट निर्मितीf(65)REVERT
test_00000004.txकॉन्ट्रॅक्ट निर्मितीf(!=65)STOP
test_00000005.txकॉन्ट्रॅक्ट निर्मितीf(!=65)f(65)REVERT
test_00000006.txकॉन्ट्रॅक्ट निर्मितीf(!=65)फॉलबॅक फंक्शनREVERT

एक्सप्लोरेशन सारांश f(!=65) हे दर्शवते की f ला 65 पेक्षा भिन्न कोणत्याही मूल्याने कॉल केले आहे.

तुम्ही पाहू शकता, Manticore प्रत्येक यशस्वी किंवा रिव्हर्ट केलेल्या ट्रान्झॅक्शनसाठी एक युनिक टेस्ट केस तयार करतो.

जर तुम्हाला जलद कोड एक्सप्लोरेशन हवे असेल तर --quick-mode फ्लॅग वापरा (तो बग डिटेक्टर, गॅस गणना, ... अक्षम करतो)

API द्वारे स्मार्ट कॉन्ट्रॅक्टमध्ये फेरफार करणे

हा विभाग Manticore Python API द्वारे स्मार्ट कॉन्ट्रॅक्टमध्ये कसे फेरफार करायचे याचे तपशील वर्णन करतो. तुम्ही *.py या python एक्सटेंशनसह नवीन फाइल तयार करू शकता आणि या फाइलमध्ये API कमांड्स (ज्यांचे मूलभूत वर्णन खाली केले जाईल) जोडून आवश्यक कोड लिहू शकता आणि नंतर $ python3 *.py या कमांडने चालवू शकता. तसेच तुम्ही खालील कमांड थेट python कंसोलमध्ये कार्यान्वित करू शकता, कंसोल चालवण्यासाठी $ python3 कमांड वापरा.

अकाउंट्स तयार करणे

पहिली गोष्ट जी तुम्ही केली पाहिजे ती म्हणजे खालील कमांडसह नवीन ब्लॉकचेन सुरू करणे:

1from manticore.ethereum import ManticoreEVM
2
3m = ManticoreEVM()

एक नॉन-कॉन्ट्रॅक्ट अकाउंट m.create_accountopens in a new tab वापरून तयार केले जाते:

1user_account = m.create_account(balance=1000)

एक Solidity कॉन्ट्रॅक्ट m.solidity_create_contractopens in a new tab वापरून तैनात केले जाऊ शकते:

1source_code = '''
2pragma solidity >=0.4.24 <0.6.0;
3contract Simple {
4 function f(uint a) payable public{
5 if (a == 65) {
6 revert();
7 }
8 }
9}
10'''
11# कॉन्ट्रॅक्ट सुरू करा
12contract_account = m.solidity_create_contract(source_code, owner=user_account)
सर्व दाखवा

सारांश

ट्रान्झॅक्शन कार्यान्वित करणे

Manticore दोन प्रकारच्या ट्रान्झॅक्शनना समर्थन देतो:

  • रॉ ट्रान्झॅक्शन: सर्व फंक्शन्स एक्सप्लोर केली जातात
  • नेम्ड ट्रान्झॅक्शन: फक्त एकच फंक्शन एक्सप्लोर केले जाते

रॉ ट्रान्झॅक्शन

एक रॉ ट्रान्झॅक्शन m.transactionopens in a new tab वापरून कार्यान्वित केले जाते:

1m.transaction(caller=user_account,
2 address=contract_account,
3 data=data,
4 value=value)

कॉलर, ॲड्रेस, डेटा, किंवा ट्रान्झॅक्शनचे मूल्य ठोस किंवा सिम्बॉलिक असू शकते:

उदाहरणार्थ:

1symbolic_value = m.make_symbolic_value()
2symbolic_data = m.make_symbolic_buffer(320)
3m.transaction(caller=user_account,
4 address=contract_address,
5 data=symbolic_data,
6 value=symbolic_value)

जर डेटा सिम्बॉलिक असेल, तर Manticore ट्रान्झॅक्शन एक्झिक्युशन दरम्यान कॉन्ट्रॅक्टची सर्व फंक्शन्स एक्सप्लोर करेल. फंक्शन सिलेक्शन कसे कार्य करते हे समजून घेण्यासाठी Hands on the Ethernaut CTFopens in a new tab लेखातील फॉलबॅक फंक्शनचे स्पष्टीकरण पाहणे उपयुक्त ठरेल.

नेम्ड ट्रान्झॅक्शन

फंक्शन्स त्यांच्या नावाद्वारे कार्यान्वित केले जाऊ शकतात. f(uint var) ला सिम्बॉलिक मूल्याने, user_account मधून, आणि 0 ईथरसह कार्यान्वित करण्यासाठी, वापरा:

1symbolic_var = m.make_symbolic_value()
2contract_account.f(symbolic_var, caller=user_account, value=0)

जर ट्रान्झॅक्शनचे value निर्दिष्ट केले नसेल, तर ते डिफॉल्टनुसार 0 असते.

सारांश

  • ट्रान्झॅक्शनचे वितर्क ठोस किंवा सिम्बॉलिक असू शकतात
  • एक रॉ ट्रान्झॅक्शन सर्व फंक्शन्स एक्सप्लोर करेल
  • फंक्शनला त्यांच्या नावाने कॉल केले जाऊ शकते

वर्कस्पेस

m.workspace ही तयार केलेल्या सर्व फाइल्ससाठी आउटपुट डिरेक्टरी म्हणून वापरली जाणारी डिरेक्टरी आहे:

1print("निकाल {} मध्ये आहेत".format(m.workspace))

एक्सप्लोरेशन समाप्त करा

एक्सप्लोरेशन थांबवण्यासाठी m.finalize()opens in a new tab वापरा. एकदा ही पद्धत कॉल केल्यावर कोणतेही पुढील ट्रान्झॅक्शन पाठवले जाऊ नयेत आणि Manticore एक्सप्लोर केलेल्या प्रत्येक पाथसाठी टेस्ट केसेस तयार करतो.

सारांश: Manticore अंतर्गत चालवणे

मागील सर्व पायऱ्या एकत्र ठेवल्यास, आपल्याला मिळते:

1from manticore.ethereum import ManticoreEVM
2
3m = ManticoreEVM()
4
5with open('example.sol') as f:
6 source_code = f.read()
7
8user_account = m.create_account(balance=1000)
9contract_account = m.solidity_create_contract(source_code, owner=user_account)
10
11symbolic_var = m.make_symbolic_value()
12contract_account.f(symbolic_var)
13
14print("निकाल {} मध्ये आहेत".format(m.workspace))
15m.finalize() # एक्सप्लोरेशन थांबवा
सर्व दाखवा

वरील सर्व कोड तुम्हाला example_run.pyopens in a new tab मध्ये मिळेल

थ्रोइंग पाथ्स मिळवणे

आता आपण f() मध्ये अपवाद निर्माण करणाऱ्या पाथसाठी विशिष्ट इनपुट तयार करू. लक्ष्य अजूनही खालील स्मार्ट कॉन्ट्रॅक्ट example.solopens in a new tab आहे:

1pragma solidity >=0.4.24 <0.6.0;
2contract Simple {
3 function f(uint a) payable public{
4 if (a == 65) {
5 revert();
6 }
7 }
8}

स्टेट माहिती वापरणे

कार्यान्वित केलेल्या प्रत्येक पाथची ब्लॉकचेनची स्वतःची स्टेट असते. एक स्टेट एकतर तयार असते किंवा ती किल्ड होते, याचा अर्थ ती THROW किंवा REVERT सूचनेपर्यंत पोहोचते:

1for state in m.all_states:
2 # स्टेटसह काहीतरी करा

तुम्ही स्टेट माहिती ऍक्सेस करू शकता. उदाहरणार्थ:

  • state.platform.get_balance(account.address): अकाउंटचा बॅलन्स
  • state.platform.transactions: ट्रान्झॅक्शनची यादी
  • state.platform.transactions[-1].return_data: शेवटच्या ट्रान्झॅक्शनद्वारे परत केलेला डेटा

शेवटच्या ट्रान्झॅक्शनद्वारे परत केलेला डेटा एक ॲरे आहे, जो ABI.deserialize सह एका मूल्यात रूपांतरित केला जाऊ शकतो, उदाहरणार्थ:

1data = state.platform.transactions[0].return_data
2data = ABI.deserialize("uint", data)

टेस्टकेस कसे तयार करावे

टेस्टकेस तयार करण्यासाठी m.generate_testcase(state, name)opens in a new tab वापरा:

1m.generate_testcase(state, 'BugFound')

सारांश

  • तुम्ही m.all_states सह स्टेटवर पुनरावृत्ती करू शकता
  • state.platform.get_balance(account.address) अकाउंटचा बॅलन्स परत करतो
  • state.platform.transactions ट्रान्झॅक्शनची यादी परत करतो
  • transaction.return_data हा परत केलेला डेटा आहे
  • m.generate_testcase(state, name) स्टेटसाठी इनपुट तयार करते

सारांश: थ्रोइंग पाथ मिळवणे

1from manticore.ethereum import ManticoreEVM
2
3m = ManticoreEVM()
4
5with open('example.sol') as f:
6 source_code = f.read()
7
8user_account = m.create_account(balance=1000)
9contract_account = m.solidity_create_contract(source_code, owner=user_account)
10
11symbolic_var = m.make_symbolic_value()
12contract_account.f(symbolic_var)
13
14## एक्झिक्युशन REVERT किंवा INVALID ने संपते का ते तपासा
15
16for state in m.terminated_states:
17 last_tx = state.platform.transactions[-1]
18 if last_tx.result in ['REVERT', 'INVALID']:
19 print('थ्रो आढळला {}'.format(m.workspace))
20 m.generate_testcase(state, 'ThrowFound')
सर्व दाखवा

वरील सर्व कोड तुम्हाला example_run.pyopens in a new tab मध्ये मिळेल

टीप: आम्ही एक अधिक सोपी स्क्रिप्ट तयार करू शकलो असतो, कारण terminated_state द्वारे परत केलेल्या सर्व स्टेट्सच्या निकालात REVERT किंवा INVALID आहे: हे उदाहरण फक्त API मध्ये कसे फेरफार करायचे हे दाखवण्यासाठी होते.

मर्यादा जोडणे

आपण एक्सप्लोरेशनवर कसे बंधन घालायचे ते पाहू. आम्ही असे गृहीत धरू की f() च्या डॉक्युमेंटेशनमध्ये असे म्हटले आहे की फंक्शनला a == 65 सह कधीही कॉल केले जात नाही, त्यामुळे a == 65 सह कोणताही बग खरा बग नाही. लक्ष्य अजूनही खालील स्मार्ट कॉन्ट्रॅक्ट example.solopens in a new tab आहे:

1pragma solidity >=0.4.24 <0.6.0;
2contract Simple {
3 function f(uint a) payable public{
4 if (a == 65) {
5 revert();
6 }
7 }
8}

ऑपरेटर्स

ऑपरेटर्सopens in a new tab मॉड्यूल मर्यादांच्या हाताळणीला सुलभ करते, इतर गोष्टींबरोबरच ते प्रदान करते:

  • Operators.AND,
  • Operators.OR,
  • Operators.UGT (unsigned greater than),
  • Operators.UGE (unsigned greater than or equal to),
  • Operators.ULT (unsigned lower than),
  • Operators.ULE (unsigned lower than or equal to).

मॉड्यूल इम्पोर्ट करण्यासाठी खालील वापरा:

1from manticore.core.smtlib import Operators

Operators.CONCAT चा वापर ॲरेला मूल्याशी जोडण्यासाठी केला जातो. उदाहरणार्थ, ट्रान्झॅक्शनच्या return_data ला एका मूल्यात बदलण्याची आवश्यकता आहे जेणेकरून ते दुसऱ्या मूल्याशी तपासले जाईल:

1last_return = Operators.CONCAT(256, *last_return)

मर्यादा

तुम्ही जागतिक स्तरावर किंवा विशिष्ट स्टेटसाठी मर्यादा वापरू शकता.

जागतिक मर्यादा

जागतिक मर्यादा जोडण्यासाठी m.constrain(constraint) वापरा. उदाहरणार्थ, तुम्ही एका सिम्बॉलिक ॲड्रेसवरून कॉन्ट्रॅक्टला कॉल करू शकता, आणि या ॲड्रेसला विशिष्ट मूल्यांवर मर्यादित करू शकता:

1symbolic_address = m.make_symbolic_value()
2m.constraint(Operators.OR(symbolic == 0x41, symbolic_address == 0x42))
3m.transaction(caller=user_account,
4 address=contract_account,
5 data=m.make_symbolic_buffer(320),
6 value=0)

स्टेट मर्यादा

एका विशिष्ट स्टेटला मर्यादा जोडण्यासाठी state.constrain(constraint)opens in a new tab वापरा. त्याच्या एक्सप्लोरेशननंतर स्टेटवर काही गुणधर्म तपासण्यासाठी मर्यादा घालण्यासाठी याचा वापर केला जाऊ शकतो.

मर्यादा तपासणे

एक मर्यादा अजूनही व्यवहार्य आहे की नाही हे जाणून घेण्यासाठी solver.check(state.constraints) वापरा. उदाहरणार्थ, खालील symbolic_value ला 65 पेक्षा वेगळे ठेवेल आणि स्टेट अजूनही व्यवहार्य आहे की नाही हे तपासेल:

1state.constrain(symbolic_var != 65)
2if solver.check(state.constraints):
3 # स्टेट व्यवहार्य आहे

सारांश: मर्यादा जोडणे

मागील कोडमध्ये मर्यादा जोडल्यास, आपल्याला मिळते:

1from manticore.ethereum import ManticoreEVM
2from manticore.core.smtlib.solver import Z3Solver
3
4solver = Z3Solver.instance()
5
6m = ManticoreEVM()
7
8with open("example.sol") as f:
9 source_code = f.read()
10
11user_account = m.create_account(balance=1000)
12contract_account = m.solidity_create_contract(source_code, owner=user_account)
13
14symbolic_var = m.make_symbolic_value()
15contract_account.f(symbolic_var)
16
17no_bug_found = True
18
19## एक्झिक्युशन REVERT किंवा INVALID ने संपते का ते तपासा
20
21for state in m.terminated_states:
22 last_tx = state.platform.transactions[-1]
23 if last_tx.result in ['REVERT', 'INVALID']:
24 # आम्ही a == 65 असलेला पाथ विचारात घेत नाही
25 condition = symbolic_var != 65
26 if m.generate_testcase(state, name="BugFound", only_if=condition):
27 print(f'बग आढळला, निकाल {m.workspace} मध्ये आहेत')
28 no_bug_found = False
29
30if no_bug_found:
31 print(f'कोणताही बग आढळला नाही')
सर्व दाखवा

वरील सर्व कोड तुम्हाला example_run.pyopens in a new tab मध्ये मिळेल

पृष्ठ अखेरचे अद्यतन: २६ एप्रिल, २०२४

हे मार्गदर्शन उपयुक्त होते का?