Analise smart-contract
27.03.2024

Skrite pasti v igri Honeypot

Pametne pogodbe Honeypot so ena od vrst goljufivih pametnih pogodb, ki so uporabnikom videti privlačne in obljubljajo finančne koristi. Vendar takšne pogodbe vsebujejo skrite pasti ali zlonamerno kodo, zaradi česar uporabniki nasprotno izgubijo svoja sredstva.

Za zaščito pred pametnimi pogodbami Honeypot morate temeljito raziskati in preveriti kodo pogodbe, preden se vključite vanjo. Svet pametnih pogodb in veriženja blokov se nenehno razvija, saj se redno pojavljajo nove ranljivosti in načini prevar, zato je za razvijalce, revizorje in uporabnike ključnega pomena, da so na tekočem z najnovejšimi trendi in razvojem na tem področju.

Primerov pametnih pogodb Honeypot je veliko. V tem članku si bomo ogledali nekaj primerov.

1. Medpot s funkcijo Fallback

V tej vrsti medpotov ima pogodba ranljivo funkcijo "rollback", ki navidezno vsakomur omogoča, da si pogodbo lasti in odstrani njeno stanje. Vendar takšna funkcija vsebuje skriti pogoj, ki preprečuje uspešno izvedbo.

Primer kode:


pragma solidity 0.4.18

contract FallbackHoneypot {
  address public owner;

  function() public payable {
      if (owner == 0) {
          owner = msg.sender;
      } else {
          revert();
      }
  }

  function withdraw() public {
      require(msg.sender == owner);
      msg.sender.transfer(this.balance);
  }
}

V tem primeru povratna funkcija omogoča kateremu koli uporabniku, da postane lastnik. Vendar operator revert() zagotavlja, da do spremembe lastnika nikoli ne pride, zaradi česar ostanejo napadalčeva sredstva ujeta v pogodbi.

2. Medeni lonček s skritim pogojem

Take pogodbe vsebujejo skriti pogoj ali zahtevo, ki mora biti izpolnjena, da je navidezno ranljiva funkcija uspešna. Uporabniki morda mislijo, da lahko pogodbo uporabijo, vendar skriti pogoj zagotavlja, da je ne morejo.

Primer kode:


pragma solidity 0.4.18

contract HiddenConditionHoneypot {
  uint256 public counter = 0;

  function deposit() public payable {
      require(msg.value == 1 ether);
      counter++;
  }

  function withdraw() public {
      require(counter > 100);
      msg.sender.transfer(this.balance);
  }
}

V tej pogodbi lahko uporabnik vplača 1 ether in zdi se, da lahko po 100 vplačilih dvigne celotno stanje pogodbe. Vendar pa pogoj counter > 100 zagotavlja, da je umik mogoč šele po 101 vplačilu, kar zadrži sredstva v pogodbi.

3. Reentrancy honeypot

V tej vrsti honeypot je pogodba ranljiva za napad reentrancy, pri katerem lahko napadalec med umikom večkrat kliče funkcijo pogodbe. Vendar pogodba vsebuje skriti mehanizem, ki preprečuje uspešno izkoriščanje.

Primer kode:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
  mapping(address => uint256) public balances;

  function deposit() public payable {
      balances[msg.sender] += msg.value;
  }

  function withdraw(uint256 _amount) public {
      require(balances[msg.sender] >= _amount);
      if (msg.sender.call.value(_amount)()) {
          balances[msg.sender] -= _amount;
      }
  }
}

V tem primeru je pogodba ranljiva za napad z vračanjem zaradi uporabe msg.sender.call.value(_amount)(). Vendar pogodba uporabnikom ne omogoča, da bi vplačali več kot začetno stanje, s čimer učinkovito zadrži njihova sredstva.

reentrancy honeypot

4. Medpot z omejitvijo plina

V tej vrsti medpotov se zdi, da pogodba uporabniku omogoča dvig sredstev, vendar količina plina, ki je potrebna za izvedbo funkcije dviga, presega omejitev bloka plina, zaradi česar transakcija ni mogoča.

Primer kode:


pragma solidity 0.4.18

contract GasLimitHoneypot {
  uint256 constant public numArrayElements = 1000;
  uint256[numArrayElements] public someArray;

  function deposit() public payable {
      for (uint256 i = 0; i < numArrayElements; i++) {
          someArray[i] = 0;
      }
  }

  function withdraw() public {
      uint256 balance = address(this).balance;
      for (uint256 i = 0; i < numArrayElements; i++) {
          someArray[i] = 1;
      }
      msg.sender.transfer(balance);
  }
}

V tem primeru funkcija depozita inicializira polje s 1000 elementi. Funkcija za izplačilo prenese pogodbeno stanje pošiljatelju, vendar spremeni elemente polja. Plin, potreben za izvedbo zanke, presega omejitev plinskega bloka, kar prepreči izplačilo in ga zadrži v pogodbi.

5. Manipulacija časovnega žiga

V tej vrsti medenega vrta pogodba kot pogoj za izvedbo uporabi časovni žig verige blokov Ethereum. Ker pa lahko rudarji manipulirajo s časovnim žigom bloka, pogodba postane ranljiva.

Primer kode:


pragma solidity 0.4.18

contract TimestampHoneypot {
  uint256 public lastInteraction;
  uint256 public prize;

  function deposit() public payable {
      lastInteraction = now;
      prize += msg.value;
  }

  function withdraw() public {
      require(now >= lastInteraction + 1 hours);
      msg.sender.transfer(prize);
      prize = 0;
  }
}

V tej pogodbi lahko uporabniki po 1 uri od zadnje interakcije vplačajo sredstva in dvignejo celotno pogodbeno stanje. Ker pa lahko rudarji manipulirajo s časovnim žigom bloka, lahko sprožijo funkcijo izplačila prej, kot je bilo pričakovano, in tako dejansko ukradejo sredstva.

6. Skriti prenos sredstev

V tej vrsti medenega vrta pogodba vsebuje skriti prenos sredstev na napadalčev naslov, ki daje videz legitimne pogodbe, v resnici pa povzroči izgubo sredstev.

Primer kode:


pragma solidity 0.4.18

contract HiddenTransferHoneypot {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function deposit() public payable {
      if (msg.value >= 1 ether) {
          owner.transfer(msg.value);
      }
  }

  function withdraw() public {
      require(msg.sender == owner);
      owner.transfer(address(this).balance);
  }
}

V tem primeru se zdi, da pogodba omogoča vplačila in izplačila. Vendar pa funkcija depozita vsebuje skriti prenos sredstev lastniku (napadalcu), če je znesek depozita enak ali večji od 1 etherja. Nič hudega sluteči uporabniki, ki bodo položili sredstva, bodo izgubili svoje etherje, saj bodo ti preneseni na napadalčev naslov.

7. Reentrancy honeypot

Reentrancy honeypot temelji na izkoriščanju ranljivosti reentrancy v pogodbi, kjer se lahko funkcija pogodbe rekurzivno kliče, preden se posodobi njeno stanje.

Primer kode:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
  mapping(address => uint256) public balances;

  function deposit() public payable {
      balances[msg.sender] += msg.value;
  }

  function withdraw() public {
      uint256 amount = balances[msg.sender];
      (bool success,) = msg.sender.call.value(amount)("");
      require(success, "Withdrawal failed.");
      balances[msg.sender] = 0;
  }
}

V tem primeru pogodba dovoljuje vplačila in izplačila. Vendar pa je ranljiva za napade s ponovnim ponavljanjem. Napadalec lahko ustvari zlonamerno pogodbo, ki rekurzivno pokliče funkcijo za izplačilo, preden je stanje nastavljeno na nič, in tako izčrpa celotno stanje pogodbe.

8. Vrstna pretvorba in prelivanje

V tej vrsti medenega vrta napadalec uporabi vrstno pretvorbo in prelivanje celih števil, da uporabnike zavede, da je pogodba varna, vendar v resnici vsebuje skrite pasti.

Primer kode:


pragma solidity 0.4.18

contract TypeCastingHoneypot {
  uint8 public count = 0;
  uint256 public reward = 1 ether;

  function increment() public payable {
      require(msg.value >= reward);

      uint8 prevCount = count;
      count++;

      if (count < prevCount) {
          msg.sender.transfer(address(this).balance);
      }
  }
}

V tem primeru pogodba nagrajuje uporabnike, ki povečajo spremenljivko count, ko pošljejo znesek, ki je enak ali večji od zneska nagrade. Vendar zaradi uporabe spremenljivke uint8 za spremenljivko count pride do prelivanja celih števil, ko vrednost doseže 255. Po prelitju se število ponastavi na 0 in napadalec lahko pridobi celotno stanje pogodbe.

overflow honeypot

9. Medpot delegatskega klica

V tej vrsti medpotov napadalec uporabi funkcijo delegatskega klica za izvajanje zlonamerne kode v imenu navidezno varne pogodbe.

Primer kode:


pragma solidity 0.4.18

contract DelegateCallHoneypot {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function() external payable {}

  function withdraw() public {
      require(msg.sender == owner);
      owner.transfer(address(this).balance);
  }

  function execute(address _target, bytes memory _data) public {
      require(msg.sender == owner);
      (bool success,) = _target.delegatecall(_data);
      require(success, "Execution failed.");
  }
}

V tem primeru pogodba omogoča vplačila, izplačila in izvajanje kode prek funkcije execute. Vendar lahko napadalec z izvajanjem funkcije delegatecall ustvari zlonamerno kodo, ki lahko manipulira s stanjem pogodbe, vključno s spremenljivko lastnik. S spremembo lastnika lahko napadalec prevzame nadzor nad pogodbo in izprazni njeno stanje.

10. Prikrito manipuliranje s pomnilnikom

V tej vrsti medovitega vrta napadalec prikrito manipulira s pomnilniškimi spremenljivkami, da uporabnike zavede v prevzem nadzora nad pogodbo.

Primer kode:


pragma solidity 0.4.18

contract HiddenStorageManipulation {
  mapping(address => uint256) public balances;
  uint256 public totalSupply;
  address public owner;

  constructor() public {
      owner = msg.sender;
      balances[owner] = 1000;
      totalSupply = 1000;
  }

  function transfer(address _to, uint256 _value) public {
      require(balances[msg.sender] >= _value);
      balances[msg.sender] -= _value;
      balances[_to] += _value;

      if (totalSupply - balances[owner] >= 1000) {
          owner = _to;
      }
  }
}

V tem primeru je pogodba videti kot preprosta pogodba s žetoni. Uporabniki si lahko med seboj posredujejo žetone, lastnik pa je na začetku nastavljen kot ustvarjalec pogodbe. Vendar lahko napadalec prikrito manipulira s spremenljivkama totalSupply in balances ter tako ponovno prevzame nadzor nad pogodbo. Ko razlika med totalSupply in balances[owner] doseže vrednost 1000, se spremenljivka owner posodobi, kar napadalcu omogoči ponoven nadzor nad pogodbo.

11. Trkanje imen funkcij

V tej vrsti medenega vrta napadalec uporabi trkanje imen funkcij, da uporabnike zavede v klic napačne funkcije.

Primer kode:


pragma solidity 0.4.18

contract FunctionNameCollision {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function withdraw() external {
      require(msg.sender == owner);
      msg.sender.transfer(address(this).balance);
  }

  function withdraw(uint256 amount) external {
      require(msg.sender == owner);
      owner.transfer(amount);
  }

  function() external payable {}
}

V tem primeru ima pogodba dve funkciji izplačila. Ena od njiju lastniku omogoča dvig celotnega stanja, druga pa dvig določenega zneska. Če pa bo uporabnik skušal dvigniti določen znesek s klicem withdraw(uint256), bo zaradi kolizije imen funkcij nehote poklical withdraw() brez argumentov, zaradi česar bo dvignil celotno stanje pogodbe.

12. Medeni lonček ranljivosti delegatskega klica

V tej vrsti medenega lončka napadalec uporabi delegatski klic za izvedbo zlonamerne funkcije v drugi pogodbi, kar lahko privede do kraje sredstev uporabnikov.

Primer kode:


pragma solidity 0.4.18

contract DelegatecallVulnerability {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function withdraw() external {
      require(msg.sender == owner);
      msg.sender.transfer(address(this).balance);
  }

  function () external payable {
      if (msg.value > 1 ether) {
          this.delegatecall(msg.data);
      }
  }
}

contract Malicious {
  function withdraw() external {
      msg.sender.transfer(address(this).balance);
  }
}           

V tem primeru je pogodba videti nedolžna, s preprosto funkcijo withdraw(), ki lastniku omogoča dvig stanja pogodbe. Vendar pogodba vsebuje tudi zasilno funkcijo, ki ob prejemu več kot 1 etherja izvede delegirani klic druge pogodbe z uporabo msg.data. Če napadalec ustvari zlonamerno pogodbo s funkcijo withdraw(), ki krade stanje pogodbe, in pošlje več kot 1 ether pogodbi žrtve, bo delegatecall izvedel zlonamerno funkcijo withdraw() in ukradel stanje pogodbe.

vulnerability honeypot

13. Medpot za prelivanje psevdointegralnih števil

Ta vrsta medpotov temelji na dejstvu, da Solidity nima vgrajene podpore za števila s plavajočo vejico. Napadalci lahko ustvarijo pogodbe, ki navzven uporabljajo decimalna števila, v resnici pa goljufivo manipulirajo s celimi števili.

Primer kode:


pragma solidity 0.4.18

contract PseudoIntegersOverflow {
  mapping(address => uint256) public balances;

  function deposit() external payable {
      require(msg.value > 0);
      uint256 pseudoValue = msg.value * 100;
      balances[msg.sender] += pseudoValue;
  }

  function withdraw(uint256 amount) external {
      uint256 pseudoAmount = amount * 100;
      require(balances[msg.sender] >= pseudoAmount);
      balances[msg.sender] -= pseudoAmount;
      msg.sender.transfer(amount);
  }
}

Ta primer daje vtis, da pogodba upravlja uporabnikovo stanje z decimalno natančnostjo, saj vrednosti pred shranjevanjem pomnoži s 100. Vendar je ta pristop zavajajoč, saj Solidity ne podpira števil s plavajočo vejico. Ko uporabnik poskuša dvigniti svoja sredstva, pogodba nepravilno izračuna psevdo vsoto zaradi skrajšanja celih števil in uporabnik morda ne bo mogel dvigniti celotnega stanja.

Da bi se zaščitili pred tovrstnimi medenimi pastmi, morate razumeti omejitve jezika Solidity in njegovo ravnanje s številskimi operacijami. Bodite previdni pri delu s pogodbami, ki trdijo, da podpirajo decimalno natančnost, in vedno preverite kodo pogodbe za morebitne težave.

14. Skrita plačila v pametnih pogodbah

Nekatere zlonamerne pametne pogodbe lahko uporabnikom naložijo skrita plačila brez njihove vednosti. Takšna plačila so lahko prikrita v kodi pogodbe ali pa se sprožijo le pod določenimi pogoji.

Primer kode:


pragma solidity 0.4.18

contract HiddenFees {
  address public owner;
  uint256 public feePercentage;

  constructor(uint256 _feePercentage) public {
      owner = msg.sender;
      feePercentage = _feePercentage;
  }

  function withdraw(uint256 amount) external {
      uint256 fee = amount * feePercentage / 100;
      uint256 netAmount = amount - fee;
      require(address(this).balance >= netAmount);
      msg.sender.transfer(netAmount);
      owner.transfer(fee);
  }
}

V tem primeru ustvarjalec pogodbe ob namestitvi pogodbe določi pristojbino (feePercentage). Pri interakciji s pogodbo uporabniki morda ne bodo vedeli za to pristojbino. Ko uporabnik dvigne svoja sredstva, pogodba izračuna pristojbino, jo odšteje od zneska dviga in preostanek pošlje uporabniku. Provizija se nato prenese na lastnika pogodbe.

Da ne bi padli v to past, skrbno preglejte kodo pogodbe in se prepričajte, da razumete vse povezane provizije. Poiščite skrita ali prikrita plačila.

15. Skrita manipulacija stanja

V nekaterih primerih lahko zlonamerne pogodbe lastniku pogodbe ali napadalcu omogočajo manipulacijo notranjega stanja pogodbe, kar povzroči nepričakovane posledice za druge uporabnike.

Primer kode:


pragma solidity 0.4.18

contract HiddenStateManipulation {
  address public owner;
  mapping(address => uint256) public balances;

  constructor() public {
      owner = msg.sender;
  }

  function deposit() external payable {
      balances[msg.sender] += msg.value;
  }

  function withdraw(uint256 amount) external {
      require(balances[msg.sender] >= amount);
      msg.sender.transfer(amount);
      balances[msg.sender] -= amount;
  }

  function manipulateBalance(address user, uint256 newBalance) external {
      require(msg.sender == owner);
      balances[user] = newBalance;
  }
}

V tem primeru pogodba uporabnikom omogoča vplačilo in izplačilo sredstev. Vendar funkcija manipulateBalance lastniku pogodbe omogoča, da poljubno spremeni stanje kateregakoli uporabnika. To lahko povzroči nepričakovane izgube za uporabnike ali lastniku pogodbe omogoči krajo sredstev.

V kodi pogodbe je treba natančno pregledati funkcije, ki omogočajo manipulacijo stanja, zlasti če ima do njih dostop samo lastnik pogodbe ali določeni naslovi.

hidden state manipulation

16. Skrita kraja žetonov

V nekaterih primerih lahko zlonamerne pogodbe vsebujejo skrite funkcije, ki lastniku pogodbe ali napadalcu omogočajo krajo žetonov nič hudega slutečim uporabnikom.

Primer kode:


pragma solidity 0.4.18

contract HiddenStateManipulation {
  address public owner;
  mapping(address => uint256) public balances;

  constructor() public {
      owner = msg.sender;
  }

  function deposit() external payable {
      balances[msg.sender] += msg.value;
  }

  function withdraw(uint256 amount) external {
      require(balances[msg.sender] >= amount);
      msg.sender.transfer(amount);
      balances[msg.sender] -= amount;
  }

  function manipulateBalance(address user, uint256 newBalance) external {
      require(msg.sender == owner);
      balances[user] = newBalance;
  }
}

V tem primeru pogodba uporabnikom omogoča, da položijo in dvignejo žetone ERC20. Funkcija stealTokens pa lastniku pogodbe omogoča, da ukrade žetone kateremu koli uporabniku.

Zaključek:

Honeypot je le ena od številnih goljufivih shem družbe DeFi, ki ne uporablja le omejitve izplačil, temveč tudi različne skrite pasti, opisane zgoraj.

Da ne bi padli na vabo, morate razumeti posebnosti goljufive sheme, subtilnosti programskega jezika Solidity in temeljito preučiti pametne pogodbe, preden z njimi sodelujete.

Z ohranjanjem informiranosti in izvedbo temeljitega skrbnega pregleda lahko zmanjšate tveganja, povezana z uporabo pametnih pogodb.

 

Če dvomite o varnosti ali funkcionalnosti pametne pogodbe, uporabite našo platformo Lotus Market.

 

 

Platformo Lotus Market je ustvarila ekipa izkušenih razvijalcev in profesionalnih revizorjev. Cilj projekta je zmanjšati tveganja vlaganja v goljufive žetone ter pomagati pri varnem in udobnem trgovanju s kriptovalutami.

 

Posredujemo, ekipa Lotus Market.

All posts

Connect to a wallet

Metamask