Cara menggunakan Echidna untuk menguji kontrak pintar
Instalasi
Echidna dapat diinstal melalui docker atau dengan menggunakan binari yang telah dikompilasi sebelumnya.
Echidna melalui docker
docker pull trailofbits/eth-security-toolboxdocker run -it -v "$PWD":/home/training 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/training
Binari
https://github.com/crytic/echidna/releases/tag/v1.4.0.0
Pengantar fuzzing berbasis properti
Echidna merupakan sebuah fuzzer berbasis properti, yang kami deskripsikan dalam postingan blog kami sebelumnya (1, 2, 3).
Fuzzing
Fuzzing adalah sebuah teknik yang terkenal dalam komunitas keamanan. Teknik ini berfungsi untuk membuat lebih banyak atau lebih sedikit input secara acak untuk menemukan bug dalam program. Fuzzer bagi perangkat lunak tradisional (seperti AFL atau LibFuzzer) dikenal sebagai peralatan efisien untuk menemukan bug.
Di luar dari pembentukan input acak secara murni, ada banyak teknik dan strategi untuk membuat input yang baik, termasuk:
- Memperoleh umpan balik dari setiap eksekusi dan generasi pemandu yang menggunakannya. Misalnya, jika sebuah input yang baru saja dibuat menghasilkan penemuan jalur baru, ini dapat menjelaskan cara membuat input baru yang dekat dengannya.
- Menghasilkan input yang sesuai dengan batasan struktural. Misalnya, jika input Anda berisi header dengan checksum, akan masuk akal untuk mengizinkan fuzzer membuat input yang memvalidasi checksum.
- Menggunakan input yang diketahui untuk membuat input baru: jika Anda memiliki akses ke kumpulan data besar input yang valid, fuzzer Anda dapat membuat input yang baru darinya, daripada memulai generasinya dari awal. Ini biasa disebut seed.
Fuzzing berbasis properti
Echidna termasuk pada keluarga fuzzer khusus: fuzzing berbasis properti yang sangat diinspirasikan oleh QuickCheck. Berlawanan dengan fuzzer klasik yang akan mencoba menemukan kesalahan, Echidna akan mencoba memecah invarian yang ditentukan oleh pengguna.
Dalam kontrak pintar, invarian adalah fungsi Solidity, yang dapat mewakili state yang salah atau tidak valid yang dapat dicapai oleh kontrak, yang mencakup:
- Kontrol akses yang salah: penyerang menjadi pemilik kontrak.
- Mesin state yang salah: token dapat ditransfer sementara kontrak dijeda.
- Aritmatika yang salah: pengguna dapat melakukan underflow terhadap saldonya dan mendapatkan token gratis yang tak terbatas.
Menguji properti dengan Echidna
Kita akan melihat cara menguji kontrak pintar dengan Echidna. Targetnya adalah kontrak pintar token.sol
berikut ini:
1contract Token{2 mapping(address => uint) public balances;3 function airdrop() public{4 balances[msg.sender] = 1000;5 }6 function consume() public{7 require(balances[msg.sender]>0);8 balances[msg.sender] -= 1;9 }10 function backdoor() public{11 balances[msg.sender] += 1;12 }13}14Tampilkan semuaSalin
Kita akan membuat asumsi bahwa token ini harus memiliki properti berikut ini:
- Siapa pun dapat memiliki maksimum 1000 token
- Token tidak dapat ditransfer (ini bukan token ERC20)
Tulis sebuah properti
Properti Echidna merupakan fungsi Solidity. Sebuah properti harus:
- Tidak memiliki argumen
- Mengembalikan
true
jika berhasil - Memiliki nama yang dimulai dengan
echidna
Echidna akan:
- Secara otomatis membuat transaksi arbitrari untuk menguji properti.
- Melaporkan transaksi mana pun yang menyebabkan properti mengembalikan
false
atau yang menghasilkan kesalahan. - Membuang efek samping saat memanggil properti (mis. jika properti mengubah variabel state, ini akan dibuang setelah tes selesai)
Properti berikut memeriksa bahwa pemanggil tidak memiliki lebih dari 1000 token:
1function echidna_balance_under_1000() public view returns(bool){2 return balances[msg.sender] <= 1000;3}4Salin
Gunakan warisan untuk memisahkan kontrak Anda dari properti Anda:
1contract TestToken is Token{2 function echidna_balance_under_1000() public view returns(bool){3 return balances[msg.sender] <= 1000;4 }5 }6Salin
token.sol
mengimplementasikan properti dan mewariskannya dari token.
Mulai sebuah kontrak
Echidna memerlukan sebuah pembangun tanpa argumen. Jika kontrak Anda memerlukan inisialisasi khusus, Anda perlu melakukannya di pembangun.
Ada beberapa alamat khusus di Echidna:
0x00a329c0648769A73afAc7F9381E08FB43dBEA72
yang memanggil pembangun.0x10000
,0x20000
, dan0x00a329C0648769a73afAC7F9381e08fb43DBEA70
yang secara acak memanggil fungsi lainnya.
Kita tidak memiliki inisialisasi khusus mana pun dalam contoh kita saat ini, akibatnya pembangun kita kosong.
Jalankan Echidna
Echidna diluncurkan dengan:
$ echidna-test contract.sol
Jika contract.sol berisi berbagai kontrak, Anda dapat menentukan target:
$ echidna-test contract.sol --contract MyContract
Ringkasan: Menguji sebuah properti
Berikut ini merangkum operasi echidna dalam contoh kita:
1contract TestToken is Token{2 constructor() public {}3 function echidna_balance_under_1000() public view returns(bool){4 return balances[msg.sender] <= 1000;5 }6 }7Salin
$ echidna-test testtoken.sol --contract TestToken...echidna_balance_under_1000: failed!