Cara menggunakan Slither untuk menemukan bug kontrak pintar
Cara menggunakan Slither
Tujuan tutorial ini adalah menunjukkan cara menggunakan Slither untuk menemukan bug dalam kontrak pintar secara otomatis.
- Instalasi
- Penggunaan baris perintah
- Pengantar analisis statis: Pengantar singkat tentang analisis statis
- API: deskripsi API Python
Instalasi
Slither memerlukan versi Python >= 3.6. Itu bisa diinstal melalui pip atau docker.
Slither melalui pip:
pip3 install --user slither-analyzer
Slither melalui docker:
docker pull trailofbits/eth-security-toolboxdocker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox
Perintah terakhirnya menjalankan kotak peralatan keamanan eth di dalam docker yang memiliki akses ke direktori Anda saat ini. Anda bisa mengubah file dari host Anda, dan menjalankan peralatannya pada file dari docker
Dalam docker, jalankan:
solc-select 0.5.11cd /home/trufflecon/
Menjalankan skrip
Untuk menjalankan skrip python dengan python 3:
python3 script.py
Baris perintah
Baris perintah versus skrip yang ditentukan pengguna. Slither dilengkapi dengan serangkaian detektor yang telah ditentukan sebelumnya yang menemukan banyak bug umum. Memanggil Slither dari baris perintah akan menjalankan semua detektor, pengetahuan mendetail tentang analisis statis tidak diperlukan:
slither project_paths
Selain detektor, Slither memiliki kemampuan meninjau kode melalui printer(opens in a new tab) dan peralatan(opens in a new tab).
Gunakan crytic.io(opens in a new tab) untuk mengakses detektor privat dan integrasi GitHub.
Analisis statis
Kemampuan dan desain kerangka kerja analisis statis Slither telah dijelaskan dalam postingan blog (1(opens in a new tab), 2(opens in a new tab)) dan sebuah makalah akademik(opens in a new tab).
Analisis statis hadir dalam rasa berbeda. Kemungkinan besar Anda menyadari bahwa pengompilasi seperti clang(opens in a new tab) dan gcc(opens in a new tab) bergantung pada teknik penelitian ini, tapi teknik ini juga mendasari (Infer(opens in a new tab), CodeClimate(opens in a new tab), FindBugs(opens in a new tab), dan peralatan berbasis metode formal seperti Frama-C(opens in a new tab) dan Polyspace(opens in a new tab).
Kita tidak akan mengulas secara lengkap teknik analisis statis dan penelitinya di sini. Sebaliknya, kita akan fokus pada apa yang diperlukan untuk memahami cara kerja Slither, sehingga Anda bisa menggunakannya dengan lebih efektif untuk menemukan bug dan memahami kode.
Representasi kode
Berbeda dengan analisis dinamis, yang menalarkan tentang jalur eksekusi tunggal, analisis statis menalarkan tentang semua jalur sekaligus. Untuk melakukannya, bergantung pada representasi kode yang berbeda. Dua representasi paling umum adalah pohon sintaksis abstrak (AST) dan grafik aliran kontrol (CFG).
Pohon Sintaksis Abstrak (AST)
AST digunakan setiap kali pengompilasi menguraikan kode. Kemungkinan ini adalah struktur paling dasar di mana analisis statis bisa dilakukan.
Singkatnya, AST adalah pohon berstruktur di mana, biasanya, tiap daunnya berisi satu variabel atau konstanta dan node internalnya adalah operand atau operasi alur kontrol. Perhatikan kode berikut:
1function safeAdd(uint a, uint b) pure internal returns(uint){2 if(a + b <= a){3 revert();4 }5 return a + b;6}Salin
AST yang sesuai ditunjukkan di dalam:
Slither menggunakan AST yang diekspor oleh solc.
Meskipun mudah untuk dibuat, AST adalah struktur bersarang. Terkadang, ini bukan yang paling mudah untuk dianalisis. Contohnya, untuk mengenali operasi yang digunakan oleh ekspresi a + b <= a
, Anda harus terlebih dahulu menganalisa <=
dan kemudian +
. Pendekatan yang umum adalah menggunakan pola pengunjung, yang menelusuri pohonnya secara berulang. Slither berisi pengunjung generik dalam ExpressionVisitor
(opens in a new tab).
Kode berikut menggunakan ExpressionVisitor
untuk mendeteksi apakah ekspresi berisi tambahan:
1from slither.visitors.expression.expression import ExpressionVisitor2from slither.core.expressions.binary_operation import BinaryOperationType34class HasAddition(ExpressionVisitor):56 def result(self):7 return self._result89 def _post_binary_operation(self, expression):10 if expression.type == BinaryOperationType.ADDITION:11 self._result = True1213visitor = HasAddition(expression) # ekspresi adalah ekspresi yang harus diuji14print(f'The expression {expression} has a addition: {visitor.result()}')Tampilkan semuaSalin
Grafik Aliran Kontrol (CFG)
Representasi kode paling umum kedua adalah grafik aliran kontrol (CFG). Seperti namanya, ini adalah representasi berbasis grafik yang menampilkan semua jalur eksekusi. Setiap node berisi satu atau beberapa instruksi. Tepi dalam grafik merepresentasikan operasi alur kontrol (jika/maka/jika tidak, perulangan, dll). CFG contoh kita sebelumnya adalah:
CFG adalah representasi yang di atasnya kebanyakan analisis dibangun.
Ada banyak representasi kode lainnya. Setiap representasi memiliki kelebihan dan kekurangan bergantung pada analisis yang ingin Anda lakukan.
Analisis
Jenis analisis paling sederhana yang dapat dilakukan dengan Slither adalah analisis sintaksis.
Analisis sintaksis
Slither bisa menelusuri berbagai komponen kode dan representasinya untuk menemukan inkonsistensi dan kelemahan dengan menggunakan pendekatan pola yang mirip.
Contohnya, detektor berikut mencari masalah yang terkait dengan sintaksis:
Bayangan variabel state(opens in a new tab): ulangi keseluruhan variabel state dan periksa apakah ada yang membayangi variabel dari kontrak yang diwariskan (state.py#L51-L62(opens in a new tab))
Antarmuka ERC20 yang salah(opens in a new tab): cari tanda tangan fungsi ERC20 yang salah (incorrect_erc20_interface.py#L34-L55(opens in a new tab))
Analisis semantik
Berbeda dengan analisis sintaksis, analisis semantik akan bergerak lebih dalam dan menganalisis "arti" kode. Keluarga ini memasukkan beberapa jenis analisis yang luas. Mereka membuat hasil yang lebih efektif dan berguna, tapi juga lebih rumit untuk ditulis.
Analisis semantik digunakan untuk deteksi kerentanan yang paling canggih.
Analisis dependensi data
Variabel variable_a
dianggap bergantung pada data variable_b
jika ada jalur yang nilai variable_a
dipengaruhi oleh variable_b
.
Dalam kode berikut, variable_a
bergantung pada variable_b
:
1// ...2variable_a = variable_b + 1;Salin
Slither hadir dengan kemampuan dependensi data(opens in a new tab) bawaan, berkat representasi menengahnya (dibahas di bagian akhir).
Satu contoh penggunaan dependensi data bisa ditemukan dalam detektor kesamaan ketat berbahaya(opens in a new tab). Di sini, Slither akan mencari perbandingan kesamaan ketat dari nilai berbahaya (incorrect_strict_equality.py#L86-L87(opens in a new tab)), dan akan memberi tahu pengguna bahwa harus menggunakan >=
atau <=
daripada ==
, untuk mencegah penyerang memerangkap kontrak. Di antara lainnya, detektor akan menganggap nilai pengembalian dari pemanggilan balanceOf(address)
berbahaya (incorrect_strict_equality.py#L63-L64(opens in a new tab)), dan akan menggunakan mesin dependensi data untuk melacak penggunaannya.
Komputasi titik tetap
Jika analisis Anda menelusuri CFG dan mengikuti tepinya, Anda mungkin akan melihat node yang sudah dikunjungi. Contohnya, jika satu perulangan ditampilkan seperti di bawah:
1for(uint i; i < range; ++){2 variable_a += 13}Salin
Analisis Anda perlu tahu kapan harus berhenti. Ada dua strategi utama di sini: (1) mengulangi setiap node beberapa kali, (2) menghitung apa yang disebut titik tetap. Titik tetap pada dasarnya berarti bahwa menganalisis node ini tidak memberi informasi berarti apa pun.
Contoh penggunaan titik tetap bisa ditemukan dalam detektor reentrancy: Slither menjelajah node, dan mencari pemanggilan eksternal, menulis dan membaca ke penyimpanan. Setelah mencapai titik tetap (reentrancy.py#L125-L131(opens in a new tab)), analisis akan menghentikan penjelajahan, dan menganalisis hasilnya untuk melihat apakah ada reentrancy, dengan menggunakan pola reentrancy berbeda (reentrancy_benign.py(opens in a new tab), reentrancy_read_before_write.py(opens in a new tab), reentrancy_eth.py(opens in a new tab)).
Menulis analisis dengan komputasi titik tetap yang efisien memerlukan pemahaman yang baik tentang cara analisis menyebarkan informasinya.
Representasi tingkat menengah
Representasi tingkat menengah (IR) adalah sebuah bahasa yang dimaksudkan agar lebih mudah diterima oleh analisis statis daripada bahasa aslinya. Slither menerjemahkan Solidity ke dalam IR-nya sendiri: SlithIR(opens in a new tab).
Memahami SlithIR tidak penting jika Anda hanya mau menulis pemeriksaan biasa. Namun, itu akan berguna jika Anda berencana menulis analisis semantik tingkat lanjut. Printer SlithIR(opens in a new tab) dan SSA(opens in a new tab) akan membantu Anda memahami cara kode diterjemahkan.
Dasar-Dasar API
Slither memiliki API yang mengizinkan Anda menjelajah atribut dasar kontrak dan fungsinya.
Untuk memuat basis kode:
1from slither import Slither2slither = Slither('/path/to/project')3Salin
Menjelajahi kontrak dan fungsi
Objek Slither
memiliki:
kontrak (daftar(Kontrak)
: daftar kontrakcontracts_derived (list(Contract)
: daftar kontrak yang tidak diwariskan oleh kontrak lainnya (subset kontrak)get_contract_from_name (str)
: Mengembalikan kontrak dari namanya
Objek Contract
memiliki:
name (str)
: Nama kontrakfunctions (list(Function))
: Daftar fungsimodifiers (list(Modifier))
: Daftar fungsiall_functions_called (list(Function/Modifier))
: Daftar semua fungsi internal yang dapat dicapai oleh kontrakinheritance (list(Contract))
: Daftar kontrak yang diwariskanget_function_from_signature (str)
: Mengembalikan Fungsi dari tanda tangannyaget_modifier_from_signature (str)
: Mengembalikan Pengubah dari tanda tangannyaget_state_variable_from_name (str)
: Mengembalikan StateVariable dari namanya
Objek Function
atau Modifier
memiliki:
name (str)
: Nama fungsicontract (contract)
: kontrak di mana fungsi dideklarasikannodes (list(Node))
: Daftar node yang membentuk CFG fungsi/pengubahentry_point (Node)
: Titik masuk CFGvariables_read (list(Variable))
: Daftar variabel yang dibacavariables_written (list(Variable))
: Daftar variabel yang ditulisstate_variables_read (list(StateVariable))
: Daftar variabel state yang dibaca (subset variabel `yang dibaca)state_variables_written (list(StateVariable))
: Daftar variabel state yang ditulis (subset variabel `yang ditulis)
Terakhir diedit: @nhsz(opens in a new tab), 15 Agustus 2023