Analyzer: Meluncur
Deskripsi: Kerangka analisis statis open-source untuk Solidity
githib: https://github.com/trailofbits/slither
Ini adalah penganalisa kode statis yang ditulis dengan python. Dia tahu bagaimana memonitor variabel, panggilan, dan mendeteksi daftar kerentanan seperti itu . Setiap kerentanan memiliki tautan dengan deskripsi, dan jika Anda baru mengenal Solidity, masuk akal jika Anda mengenal semua orang.
Slither dapat berfungsi sebagai modul python dan menyediakan antarmuka bagi programmer untuk mengaudit sesuai dengan rencananya sendiri. Contoh sederhana dan ilustratif tentang apa yang bisa dilakukan oleh slither dapat dilihat di sini .
Kami akan kembali ke skenario analisis di akhir artikel, tetapi untuk saat ini jalankan Slither:
git clone https://github.com/trailofbits/slither.git cd slither docker build -t slither .
dan mencoba menganalisis kontrak kami.
Kami masuk ke direktori dengan constructor-eth-booking dan mencoba menjalankan slither dari buruh pelabuhan:
$ docker run -v $(pwd)/contracts:/slither/contracts slither contracts/flattened.sol
kita mendapatkan kesalahan "File sumber memerlukan versi kompiler yang berbeda", dan sekarang kita perlu memasukkan versi solc=0.4.20
ke solc=0.4.20
docker. Untuk melakukan ini, edit Dockerfile of Slither itu sendiri, seperti yang ditunjukkan pada Bagian 2 dalam pendahuluan , yaitu. di suatu tempat di ujung Dockerfile kami menambahkan baris:
COPY --from=ethereum/solc:0.4.20 /usr/bin/solc /usr/bin
, membangun kembali gambar, menjalankan, sorakan, semuanya mengkompilasi.
Kita melihat output, peringatan tentang "pragma" yang berbeda dan tentang nama variabel yang salah seperti Parameter '_price' of Booking.Booking (flattened.sol#73) is not in mixedCase
. Analisis memberikan banyak peringatan, tetapi kami mencari bug nyata, dan kami tidak akan memperhatikan hal sepele. Kami menyaring semua pesan tentang mixedCase, sekarang tidak sesuai dengan gaya:
$ docker run -v $(pwd)/contracts:/slither/contracts slither contracts/flattened.sol 2>&1 | fgrep -v 'mixedCase'
Programmer sejati kehilangan semua warna hijau, terlihat merah semuanya, dan inilah yang ditemukan Slither dalam kontrak selain false-positive:
Booking.refundWithoutCancellationFee (flattened.sol#243-250) sends eth to arbirary user Dangerous calls: - client.transfer(address(this).balance) (flattened.sol#249) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations INFO:Detectors: Booking.refundWithCancellationFee (flattened.sol#252-259) sends eth to arbirary user Dangerous calls: - owner.transfer(m_cancellationFee) (flattened.sol#257) - client.transfer(address(this).balance) (flattened.sol#258) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations
Sekarang lihat apa yang salah dengan fungsi ini dalam kontrak:
/************************** PRIVATE **********************/ function refundWithoutCancellationFee() private { address client = m_client; m_client = address(0); changeState(State.OFFER); client.transfer(address(this).balance); } function refundWithCancellationFee() private { address client = m_client; m_client = address(0); changeState(State.CANCELED); owner.transfer(m_cancellationFee); client.transfer(address(this).balance); }
dalam hal ini, misalnya, fungsi refundWithoutCancellationFee()
dipanggil seperti ini:
function rejectPayment() external onlyOwner onlyState(State.PAID) { refundWithoutCancellationFee(); } function refund() external onlyClient onlyState(State.PAID) { refundWithoutCancellationFee(); }
Hmm, secara formal tidak ada kesalahan: panggilan dilindungi oleh semua jenis onlyOwner
, tetapi Slither bersumpah bahwa, kata mereka, siaran dikirim tanpa ada pengecekan di dalam pengembalian uang. Dan dia benar, fungsinya sendiri hampir tidak memiliki batasan. Biarkan itu bersifat pribadi, dan dipanggil dari pembungkus "rejectPayment ()" "refund ()" dengan batasan yang diperlukan, tetapi dalam formulir ini, jika Anda memodifikasi kontrak, ada risiko besar melupakan pembatasan dan menempelkan pengembalian refundWithoutCancellationFee()
. tersedia untuk penyerang. Jadi, bahkan jika secara resmi tidak ada kerentanan, informasi tersebut ternyata berguna - ini setidaknya merupakan tingkat "peringatan", jika penugasan berencana untuk mengembangkan kode kontrak lebih lanjut. Dalam hal ini, dua fungsi dari peserta yang berbeda menggunakan kode yang sama, dan keputusan ini dibuat untuk menghemat gas - kontrak satu kali, dan biaya perhitungannya merupakan faktor penting.
Saya memeriksa kembali bahwa Slither mengutuk penyiaran apa pun, dan mentransfer badan fungsi langsung ke "rejectPayment ()" dan "refund ()" di atas, peringatan itu hilang, yaitu, Slither menyadari bahwa sekarang siaran tidak dikirim tanpa pemeriksaan alamat. Awal yang bagus!
Sekarang mari kita periksa bagaimana Slither memantau inisialisasi variabel, untuk ini kami mengomentari dua inisialisasi:
- m_fileHash = _fileHash; + // m_fileHash = _fileHash; - m_price = _price; + // m_price = _price;
yang pertama tidak terlalu penting, dalam hal lubang, kecuali untuk membuang-buang sumber daya, karena m_fileHash tidak digunakan di mana pun, itu hanya disimpan di blockchain ketika membuat kontrak. Tapi m_price digunakan, dan Slither benar bersumpah bahwa m_price tidak diinisialisasi di mana pun, meskipun digunakan:
Booking.m_price (flattened.sol#128) is never initialized. It is used in: - fallback (flattened.sol#144-156)
Nah ini adalah trik sederhana, seperti yang diharapkan, semuanya bekerja dengan baik.
Sekarang kami akan menambahkan reentranc yang sangat disukai semua orang dalam kontrak: kami akan mengubah status kontrak setelah panggilan eksternal. Kami membuat perubahan berikut:
function refundWithoutCancellationFee() private { address client = m_client; - m_client = address(0); - changeState(State.OFFER); - client.transfer(address(this).balance); + client.call.value(address(this).balance)(); + m_client = address(0); + changeState(State.OFFER); }
Saya harus mengganti transfer dengan panggilan, karena varian transfer tidak bersumpah karena transfer mengirim panggilan dengan gas minimum, dan callback tidak mungkin (walaupun ketika beralih ke garpu Konstantinopel di Ethereum, harga gas diubah dan ini mengaktifkan kembali serangan reentrancy menggunakan transfer .
Reentrancy hasil pencarian:
Reentrancy in Booking.refundWithoutCancellationFee (flattened.sol#243-253): External calls: - client.call.value(address(this).balance)() (flattened.sol#245) State variables written after the call(s): - m_client (flattened.sol#246)
Tidak apa-apa, setidaknya itu tidak akan memberikan variabel keadaan setelah panggilan eksternal, dan ini sangat bagus.
Jika Anda bergerak di sekitar daftar, maka kerentanan lain dalam daftar hanya mencari metode tertentu dalam kode, atau pola yang diketahui, yang, tentu saja, jika Anda memiliki akses ke markup kode yang dilakukan oleh python, bekerja dengan cukup andal. Yaitu baik-tahu pola Slither tidak akan ketinggalan.
Sekarang, saya akan membuat perubahan yang dengan sempurna menunjukkan spesifikasi pekerjaan analisa statis:
- client.transfer(address(this).balance + for (uint i=0; i < 1; i++) { + client.transfer(address(this).balance - 999999999999999999); + }
dan hasilnya:
Booking.refundWithoutCancellationFee has external calls inside a loop: - client.transfer(address(this).balance - 999999999999999999) (flattened.sol#252) Reference: https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description/_edit#calls-inside-a-loop
Siklus dijalankan sekali, dan mengalami degenerasi - oleh karena itu, peringatan yang dikeluarkan adalah positif palsu, dan tidak adanya peringatan tentang aritmatika berbahaya adalah negatif palsu. Analisis jenis, hasil operasi, penghitungan panggilan - tugas bukan untuk analisis statis. Karenanya, pahami dengan jelas kesalahan mana yang akan ditemukan Slither dan mana yang harus dicari menggunakan alat lain.
Kami berjanji untuk menyebutkan kemungkinan penulisan skrip kami sendiri untuk pengujian, dan output dari setiap informasi menarik tentang kontrak menggunakan kunci --print
. Dari sudut pandang ini, Slither adalah alat yang hebat untuk CI. Pengembang sistem kontrak besar mengetahui nama-nama variabel keselamatan-kritis: saldo, ukuran komisi, bendera, dan dapat menulis skrip pengujian yang akan memblokir setiap perubahan dalam kode yang, misalnya, menimpa variabel penting, atau mengubah variabel status setelah panggilan eksternal, dan analisisnya yang sangat mudah diprediksi adalah alat yang hebat untuk digunakan dalam kaitan.
Tugas Slither adalah untuk menyelamatkan Anda dari bug bodoh, menemukan pola berbahaya yang terkenal dan memperingatkan pengembang. Dalam versi ini, itu baik sebagai alat untuk pengembang pemula di Solidity, segera meminta cara menulis kode dalam Solidity dengan benar.
Ringkasan
Dalam pengujian pribadi saya, saya akan menempatkan Slither empat untuk keserbagunaan, kesederhanaan dan kemudahan penggunaan, serta untuk skrip pengujian yang mudah dimengerti dan kemampuan beradaptasi dengan CI.
Slither dengan percaya diri menemukan PERINGATAN nyata yang terkait dengan penggunaan fungsi pengiriman siaran, ia menemukan semua bug yang diperkenalkan. Dia tidak bisa mengatasi hanya dengan analisis dinamis, yang secara formal tidak boleh dilakukan, kalau tidak, dia harus mengorbankan universalitas, prediktabilitas, dan kemudahan penggunaan.
Pada artikel berikutnya, kita akan berurusan dengan penganalisa Mythril, tetapi di sini adalah daftar isi untuk artikel yang siap atau direncanakan untuk ditulis:
Bagian 1. Pendahuluan. Kompilasi, perataan, versi Solidity
Bagian 2. Slither (artikel ini)
Bagian 3. Mythril
Bagian 4. Manticore (dalam proses penulisan)
Bagian 5. Echidna (dalam proses penulisan)