Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
SoliditySecurity — Solidity Security | Kitploit
Strumenti/GitHubGitHub/al1ex/soliditysecurity
Vulnerability AnalysisCode AnalysisLearning & EducationCurated Resources
GitHubal1ex/soliditysecurity

SoliditySecurity

Solidity Security

Vedi Repository
355 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Cos'è questo

Questo post si propone di essere un post introduttivo relativamente approfondito e aggiornato che descrive gli errori passati commessi dagli sviluppatori di Solidity, nel tentativo di impedire ai futuri sviluppatori di ripetere la storia.

Indice

1. Re-Entrancy

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: The DAO

2. Overflow/Underflow Aritmetici

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: PoWHC e Batch Transfer Overflow (CVE-2018-10299)

3. Ether Inaspettato

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Sconosciuti

4. Delegatecall

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Parity Multisig Wallet (Secondo Hack)

5. Visibilità di Default

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: Parity MultiSig Wallet (Primo Hack)

6. Illusione dell'Entropia

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: Contratti PRNG

7. Riferimento a Contratti Esterni

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: Re-Entrancy Honey Pot

8. Attacco Short Address/Parametro

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Sconosciuti

9. Valori di Ritorno CALL Non Controllati

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Etherpot e King of the Ether

10. Race Conditions / Front Running

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: ERC20 e Bancor

11. Denial Of Service (DOS)

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: GovernMental

12. Manipolazione del Timestamp di Blocco

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: GovernMental

13. Costruttori con Cautela

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: Rubixi

14. Puntatori di Storage Non Inizializzati

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Honey Pots: OpenAddressLottery e CryptoRoulette

15. Numeri a Virgola Mobile e Precisione Numerica

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempio Reale: Ethstick

16. Autenticazione tx.origin

  • La Vulnerabilità
  • Tecniche di Prevenzione
  • Esempi Reali: Sconosciuti

Stranezze di Ethereum

  • Ether Senza Chiave
  • Indirizzi Monouso
  • Airdrop a Transazione Singola

Elenco di Hack/Bug Interessanti Legati alle Criptovalute

Riferimenti / Elenco di Ulteriori Letture

  • Ethereum Wiki - Sicurezza
  • Solidity Docs - Considerazioni sulla Sicurezza
  • Consensus - Ethereum Smart Contract Best Practices
  • Storia delle Vulnerabilità di Sicurezza di Ethereum, Hack e Relative Correzioni
  • Progetto per la Sicurezza delle Applicazioni Decentralizzate (DASP) Top 10 del 2018
  • Un'indagine sugli attacchi agli Smart Contract di Ethereum
  • Sicurezza degli Smart Contract di Ethereum
  • Lezioni apprese dal Concorso Solidity Underhanded

1. Re-Entrancy

Una delle caratteristiche degli smart contract di Ethereum è la capacità di chiamare e utilizzare codice di altri contratti esterni. I contratti inoltre gestiscono tipicamente ether e, come tali, inviano spesso ether a vari indirizzi utente esterni. L'operazione di chiamare contratti esterni, o di inviare ether a un indirizzo, richiede che il contratto effettui una chiamata esterna. Queste chiamate esterne possono essere dirottate dagli attaccanti, i quali costringono il contratto a eseguire ulteriore codice (ad esempio tramite una funzione fallback), incluse chiamate di ritorno verso se stesso. L'esecuzione del codice "ri-entra" così nel contratto. Attacchi di questo tipo sono stati utilizzati nel famigerato hack di The DAO.

Per ulteriori letture sugli attacchi di re-entrancy, vedi Reentrancy Attack On Smart Contracts e Consensus - Ethereum Smart Contract Best Practices.

La Vulnerabilità

Questo attacco può verificarsi quando un contratto invia ether a un indirizzo sconosciuto. Un attaccante può costruire con cura un contratto a un indirizzo esterno che contenga codice dannoso nella funzione fallback. Pertanto, quando un contratto invia ether a questo indirizzo, verrà invocato il codice dannoso. In genere il codice dannoso esegue una funzione sul contratto vulnerabile, compiendo operazioni non previste dallo sviluppatore. Il nome "re-entrancy" deriva dal fatto che il contratto esterno dannoso richiama una funzione sul contratto vulnerabile e "ri-entra" nell'esecuzione del codice in un punto arbitrario del contratto vulnerabile.

Per chiarire questo concetto, si consideri il semplice contratto vulnerabile, che funge da caveau Ethereum e consente ai depositanti di prelevare solo 1 ether a settimana.

EtherStore.sol:```solidity contract EtherStore {

root@kitploit:~
uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;

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

function withdrawFunds (uint256 _weiToWithdraw) public {
    require(balances[msg.sender] >= _weiToWithdraw);
    // limit the withdrawal
    require(_weiToWithdraw <= withdrawalLimit);
    // limit the time allowed to withdraw
    require(now >= lastWithdrawTime[msg.sender] + 1 weeks);
    require(msg.sender.call.value(_weiToWithdraw)());
    balances[msg.sender] -= _weiToWithdraw;
    lastWithdrawTime[msg.sender] = now;
}

}

root@kitploit:~
Questo contratto ha due funzioni pubbliche. `depositFunds()` e `withdrawFunds()`. La funzione `depositFunds()` incrementa semplicemente i saldi dei mittenti. La funzione `withdrawFunds()` consente al mittente di specificare l'importo di wei da prelevare. Avrà successo solo se l'importo richiesto da prelevare è inferiore a 1 ether e non è stato effettuato un prelievo nell'ultima settimana. O no?...

La vulnerabilità si trova alla riga \[17\] dove inviamo all'utente l'importo di ether richiesto. Considera un attaccante malintenzionato che crea il seguente contratto,

Attack.sol:```solidity
import "EtherStore.sol";

contract Attack {
  EtherStore public etherStore;

  // initialise the etherStore variable with the contract address
  constructor(address _etherStoreAddress) {
      etherStore = EtherStore(_etherStoreAddress);
  }

  function pwnEtherStore() public payable {
      // attack to the nearest ether
      require(msg.value >= 1 ether);
      // send eth to the depositFunds() function
      etherStore.depositFunds.value(1 ether)();
      // start the magic
      etherStore.withdrawFunds(1 ether);
  }

  function collectEther() public {
      msg.sender.transfer(this.balance);
  }

  // fallback function - where the magic happens
  function () payable {
      if (etherStore.balance > 1 ether) {
          etherStore.withdrawFunds(1 ether);
      }
  }
}

Let us see how this malicious contract can exploit our EtherStore contract. The attacker would create the above contract (let's say at the address 0x0...123) with the EtherStore's contract address as the constructor parameter. This will initialize and point the public variable etherStore to the contract we wish to attack.

The attacker would then call the pwnEtherStore() function, with some amount of ether (greater than or equal to 1), let's say 1 ether for this example. In this example we assume a number of other users have deposited ether into this contract, such that it's current balance is 10 ether. The following would then occur:

  1. Attack.sol - Riga [15] - La funzione depositFunds() del contratto EtherStore verrà chiamata con un msg.value di 1 ether (e molto gas). Il mittente (msg.sender) sarà il nostro contratto malevolo (0x0...123). Quindi, balances[0x0..123] = 1 ether.

  2. Attack.sol - Riga [17] - Il contratto malevolo chiamerà quindi la funzione withdrawFunds() del contratto EtherStore con un parametro di 1 ether. Questo supererà tutti i requisiti (Righe [12]-[16] del contratto EtherStore) poiché non abbiamo effettuato prelievi precedenti.

  3. EtherStore.sol - Riga [17] - Il contratto invierà quindi 1 ether al contratto malevolo.

Il risultato finale è che l'attaccante ha prelevato tutto l'ether (tranne 1) dal contratto EtherStore, istantaneamente con una singola transazione.

Tecniche di Prevenzione

Esistono diverse tecniche comuni che aiutano a evitare potenziali vulnerabilità di rientranza nei contratti intelligenti. La prima è (quando possibile) utilizzare la funzione integrata transfer() quando si invia ether a contratti esterni. La funzione transfer invia solo 2300 gas con la chiamata esterna, che non è sufficiente per consentire all'indirizzo/contratto di destinazione di chiamare un altro contratto (cioè di rientrare nel contratto chiamante).

La seconda tecnica consiste nel garantire che tutta la logica che modifica le variabili di stato venga eseguita prima che l'ether venga inviato all'esterno del contratto (o prima di qualsiasi chiamata esterna). Nell'esempio EtherStore, le righe [18] e [19] di EtherStore.sol dovrebbero essere poste prima della riga [17]. È buona pratica inserire qualsiasi codice che effettui chiamate esterne a indirizzi sconosciuti come ultima operazione in una funzione o porzione di codice localizzata. Questo è noto come pattern checks-effects-interactions.

Una terza tecnica consiste nell'introdurre un mutex. Cioè, aggiungere una variabile di stato che blocchi il contratto durante l'esecuzione del codice, impedendo chiamate di rientranza.

Applicando tutte queste tecniche (tutte e tre non sono necessarie, ma vengono usate a scopo dimostrativo) a EtherStore.sol, si ottiene il contratto senza rientranze:```solidity contract EtherStore {

root@kitploit:~
// initialise the mutex
bool reEntrancyMutex = false;
uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;

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

function withdrawFunds (uint256 _weiToWithdraw) public {
    require(!reEntrancyMutex);
    require(balances[msg.sender] >= _weiToWithdraw);
    // limit the withdrawal
    require(_weiToWithdraw <= withdrawalLimit);
    // limit the time allowed to withdraw
    require(now >= lastWithdrawTime[msg.sender] + 1 weeks);
    balances[msg.sender] -= _weiToWithdraw;
    lastWithdrawTime[msg.sender] = now;
    // set the reEntrancy mutex before the external call
    reEntrancyMutex = true;
    msg.sender.transfer(_weiToWithdraw);
    // release the mutex after the external call
    reEntrancyMutex = false;
}

}

root@kitploit:~
<h3 id="re-example">Esempio reale: The DAO</h3>

[The DAO](https://en.wikipedia.org/wiki/The_DAO_(organization)) (Organizzazione Autonoma Decentralizzata) è stato uno dei maggiori hack avvenuti nelle prime fasi di sviluppo di Ethereum. All'epoca, il contratto deteneva oltre 150 milioni di dollari USA. La re-entrancy ha giocato un ruolo fondamentale nell'attacco che alla fine ha portato all'hard-fork che ha creato Ethereum Classic (ETC). Per una buona analisi dell'exploit del DAO, vedi [il post di Phil Daian](http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/).

<h2 id="ouflow"><span id="SP-2">2. Overflow e Underflow Aritmetici</span></h2>

La Ethereum Virtual Machine (EVM) specifica tipi di dati a dimensione fissa per gli interi. Ciò significa che una variabile intera può rappresentare solo un certo intervallo di numeri. Un `uint8`, ad esempio, può memorizzare solo numeri nell'intervallo \[0,255\]. Tentare di memorizzare `256` in un `uint8` restituirà `0`. Se non si presta attenzione, le variabili in Solidity possono essere sfruttate se l'input dell'utente non viene controllato e vengono eseguite operazioni che producono numeri al di fuori dell'intervallo del tipo di dati che li memorizza.

Per ulteriori letture su overflow e underflow aritmetici, vedi [Come proteggere i tuoi Smart Contract](https://medium.com/loom-network/how-to-secure-your-smart-contracts-6-solidity-vulnerabilities-and-how-to-avoid-them-part-1-c33048d4d17d), [Migliori pratiche per gli Smart Contract Ethereum](https://consensys.github.io/smart-contract-best-practices/known_attacks/#integer-overflow-and-underflow) e [Ethereum, Solidity e gli integer overflow: programmare blockchain come nel 1970](https://randomoracle.wordpress.com/2018/04/27/ethereum-solidity-and-integer-overflows-programming-blockchains-like-1970/)

<h3 id="ou-vuln">La vulnerabilità</h3>

Un overflow/underflow si verifica quando viene eseguita un'operazione che richiede a una variabile a dimensione fissa di memorizzare un numero (o un dato) che è al di fuori dell'intervallo del tipo di dati della variabile.

Ad esempio, sottrarre `1` da una variabile `uint8` (intero senza segno a 8 bit, cioè solo positivo) che memorizza `0` come valore, restituirà il numero `255`. Questo è un underflow. Abbiamo assegnato un numero al di sotto dell'intervallo del `uint8`; il risultato *si avvolge su sé stesso* e fornisce il numero più grande che un `uint8` può memorizzare. Allo stesso modo, aggiungere `2^8=256` a un `uint8` lascerà la variabile invariata, poiché abbiamo percorso l'intera lunghezza dell'`uint` (per i matematici, questo è simile ad aggiungere $2\pi$ all'angolo di una funzione trigonometrica, $\sin(x) = \sin(x+2\pi)$). Aggiungere numeri più grandi dell'intervallo del tipo di dati è chiamato overflow. Per chiarezza, aggiungere `257` a un `uint8` che attualmente ha valore zero restituirà il numero `1`. A volte è istruttivo pensare alle variabili a tipo fisso come cicliche, dove si ricomincia da zero se si aggiungono numeri oltre il numero più grande memorizzabile, e viceversa per lo zero (dove si inizia a contare alla rovescia dal numero più grande man mano che si sottrae da 0).

Questi tipi di insidie numeriche consentono agli aggressori di abusare del codice e di creare flussi logici imprevisti. Ad esempio, considera il contratto di blocco temporale qui sotto.

TimeLock.sol:```solidity
contract TimeLock {

    mapping(address => uint) public balances;
    mapping(address => uint) public lockTime;

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

    function increaseLockTime(uint _secondsToIncrease) public {
        lockTime[msg.sender] += _secondsToIncrease;
    }

    function withdraw() public {
        require(balances[msg.sender] > 0);
        require(now > lockTime[msg.sender]);
        uint transferValue = balances[msg.sender];
        balances[msg.sender] = 0;
        msg.sender.transfer(transferValue);
    }
}

Questo contratto è progettato per agire come una cassetta di sicurezza temporale, in cui gli utenti possono depositare ether nel contratto e sarà bloccato lì per almeno una settimana. L'utente può estendere il tempo di attesa a più di 1 settimana se lo desidera, ma una volta depositato, l'utente può essere certo che il proprio ether è bloccato in sicurezza per almeno una settimana. O possono?...

Nel caso in cui un utente sia costretto a consegnare la propria chiave privata (pensa a una situazione di ostaggio), un contratto come questo può essere utile per garantire che l'ether non sia ottenibile in brevi periodi di tempo. Se un utente avesse bloccato 100 ether in questo contratto e avesse consegnato le proprie chiavi a un attaccante, quest'ultimo potrebbe usare un overflow per ricevere l'ether, indipendentemente dal lockTime.

L'attaccante potrebbe determinare il lockTime corrente per l'indirizzo di cui ora possiede la chiave (è una variabile pubblica). Chiamiamolo userLockTime. Potrebbe quindi chiamare la funzione increaseLockTime e passare come argomento il numero 2^256 - userLockTime. Questo numero verrebbe aggiunto al userLockTime corrente e causerebbe un overflow, azzerando lockTime[msg.sender] a 0. L'attaccante potrebbe quindi semplicemente chiamare la funzione withdraw per ottenere la propria ricompensa.

Diamo un'occhiata a un altro esempio, tratto dalle Ethernaut Challanges.

ALLERTA SPOILER: Se non hai ancora svolto le sfide di Ethernaut, questo fornisce una soluzione a uno dei livelli.```solidity pragma solidity ^0.4.18;

contract Token {

mapping(address => uint) balances; uint public totalSupply;

function Token(uint _initialSupply) { balances[msg.sender] = totalSupply = _initialSupply; }

function transfer(address _to, uint _value) public returns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; }

function balanceOf(address _owner) public constant returns (uint balance) { return balances[_owner]; } }

root@kitploit:~
Questo è un semplice contratto token che impiega una funzione `transfer()`, consentendo ai partecipanti di spostare i propri token. Riesci a vedere l'errore in questo contratto?

Il difetto si trova nella funzione `transfer()`. L'istruzione require alla riga \[13\] può essere elusa utilizzando un underflow. Considera un utente che non ha saldo. Potrebbe chiamare la funzione `transfer()` con qualsiasi `_value` diverso da zero e superare l'istruzione require alla riga \[13\]. Questo perché `balances[msg.sender]` è zero (e un `uint256`), quindi sottrarre qualsiasi importo positivo (escluso `2^256`) risulterà in un numero positivo a causa dell'underflow descritto sopra. Questo vale anche per la riga \[14\], dove il nostro saldo verrà accreditato con un numero positivo. Quindi, in questo esempio, abbiamo ottenuto token gratuiti a causa di una vulnerabilità di underflow.

<h3 id="ou-prevention">Tecniche di prevenzione</h3>

La tecnica (attualmente) convenzionale per proteggersi dalle vulnerabilità di under/overflow è usare o costruire librerie matematiche che sostituiscono gli operatori matematici standard; addizione, sottrazione e moltiplicazione (la divisione è esclusa perché non causa over/under flow e l'EVM fa revert in caso di divisione per 0).

[OppenZepplin](https://github.com/OpenZeppelin/zeppelin-solidity) hanno fatto un ottimo lavoro nel costruire e verificare librerie sicure che possono essere sfruttate dalla comunità Ethereum. In particolare, la loro [Safe Math Library](https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol) è un riferimento o una libreria da usare per evitare vulnerabilità di under/over flow.

Per dimostrare come queste librerie vengono usate in Solidity, correggiamo il contratto `TimeLock`, usando la libreria `SafeMath` di Open Zepplin. Il contratto senza overflow diventerebbe:```solidity
library SafeMath {

  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}

contract TimeLock {
    using SafeMath for uint; // use the library for uint type
    mapping(address => uint256) public balances;
    mapping(address => uint256) public lockTime;

    function deposit() public payable {
        balances[msg.sender] = balances[msg.sender].add(msg.value);
        lockTime[msg.sender] = now.add(1 weeks);
    }

    function increaseLockTime(uint256 _secondsToIncrease) public {
        lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease);
    }

    function withdraw() public {
        require(balances[msg.sender] > 0);
        require(now > lockTime[msg.sender]);
        uint transferValue = balances[msg.sender];
        balances[msg.sender] = 0;
        msg.sender.transfer(transferValue);
    }
}

Nota che tutte le operazioni matematiche standard sono state sostituite da quelle definite nella libreria SafeMath. Il contratto TimeLock non esegue più alcuna operazione che possa causare un under/overflow.

Esempi reali: PoWHC e overflow di Batch Transfer (CVE-2018–10299)

Un gruppo di 4chan decise che era un'ottima idea costruire uno schema Ponzi su Ethereum, scritto in Solidity. Lo chiamarono Proof of Weak Hands Coin (PoWHC). Sfortunatamente, sembra che gli autori del contratto non avessero mai visto underflow/overflow prima e, di conseguenza, 866 ether furono sottratti dal suo contratto. Una buona panoramica di come si verifica l'underflow (che non è molto diverso dalla sfida Ethernaut di cui sopra) è fornita nel post di Eric Banisadar.

Alcuni sviluppatori hanno anche implementato una funzione batchTransfer() in alcuni contratti token ERC20. L'implementazione conteneva un overflow. Questo post lo spiega, tuttavia penso che il titolo sia fuorviante, poiché non ha nulla a che fare con lo standard ERC20, ma piuttosto alcuni contratti token ERC20 hanno una funzione batchTransfer() vulnerabile implementata.

3. Ether inatteso

Tipicamente, quando l'ether viene inviato a un contratto, deve eseguire o la funzione fallback o un'altra funzione descritta nel contratto. Ci sono due eccezioni a questo, in cui l'ether può esistere in un contratto senza che sia stato eseguito alcun codice. I contratti che fanno affidamento sull'esecuzione del codice per ogni ether inviato al contratto possono essere vulnerabili ad attacchi in cui l'ether viene inviato forzatamente a un contratto.

Per ulteriori letture su questo, vedi Come proteggere i tuoi Smart Contract: 6 e Solidity security patterns - forzare l'ether a un contratto .

La vulnerabilità

Una tecnica di programmazione difensiva comune che è utile per imporre transizioni di stato corrette o per validare operazioni è il controllo degli invarianti. Questa tecnica consiste nel definire un insieme di invarianti (metriche o parametri che non dovrebbero cambiare) e nel verificare che questi invarianti rimangano invariati dopo una o più operazioni. Questo è tipicamente un buon design, a condizione che gli invarianti controllati siano effettivamente invarianti. Un esempio di invariante è il totalSupply di un token ERC20 a emissione fissa. Poiché nessuna funzione dovrebbe modificare questo invariante, si potrebbe aggiungere un controllo alla funzione transfer() che assicuri che totalSupply rimanga invariato per garantire che la funzione funzioni come previsto.

In particolare, c'è un invariante apparente, che può essere allettante da usare ma che in realtà può essere manipolato da utenti esterni (indipendentemente dalle regole stabilite nello smart contract) .Questo è l'ether corrente memorizzato nel contratto. Spesso quando gli sviluppatori imparano per la prima volta Solidity hanno l'idea errata che un contratto possa accettare o ottenere ether solo tramite funzioni payable. Questa errata convinzione può portare a contratti che hanno false supposizioni sul saldo di ether al loro interno, il che può portare a una serie di vulnerabilità. La prova decisiva di questa vulnerabilità è l'uso (scorretto) di this.balance. Come vedremo, gli usi scorretti di this.balance possono portare a gravi vulnerabilità di questo tipo.

Ci sono due modi in cui l'ether può essere (forzatamente) inviato a un contratto senza usare una funzione payable o eseguire alcun codice sul contratto. Questi sono elencati di seguito.

Autodistruzione / Suicidio

Qualsiasi contratto è in grado di implementare la funzione selfdestruct(address), che rimuove tutto il bytecode dall'indirizzo del contratto e invia tutto l'ether ivi memorizzato all'indirizzo specificato come parametro. Se questo indirizzo specificato è anche un contratto, nessuna funzione (inclusa la fallback) viene chiamata. Pertanto, la funzione selfdestruct() può essere utilizzata per inviare forzatamente ether a qualsiasi contratto, indipendentemente da qualsiasi codice possa esistere nel contratto. Questo include anche i contratti senza funzioni payable. Ciò significa che qualsiasi attaccante può creare un contratto con una funzione selfdestruct(), inviargli ether, chiamare selfdestruct(target) e forzare l'invio di ether a un contratto target. Martin Swende ha un eccellente post sul blog che descrive alcune particolarità dell'opcode self-destruct (Quirk #2) insieme a una descrizione di come i nodi client stessero controllando invarianti errati che avrebbero potuto portare a una piuttosto catastrofica distruzione dei client.

Ether pre-inviato

Il secondo modo in cui un contratto può ottenere ether senza usare una funzione selfdestruct() o chiamare funzioni payable è pre-caricare l'indirizzo del contratto con ether. Gli indirizzi dei contratti sono deterministici; infatti l'indirizzo è calcolato dall'hash keccak256 (a volte sinonimo di SHA3) dell'indirizzo che crea il contratto e del nonce della transazione che crea il contratto. Nello specifico, è della forma: address = sha3(rlp.encode([account_address,transaction_nonce])) (vedi Keyless Ether per alcuni casi d'uso divertenti di questo). Ciò significa che chiunque può calcolare quale sarà l'indirizzo di un contratto prima che venga creato e quindi inviare ether a quell'indirizzo. Quando il contratto viene effettivamente creato, avrà un saldo di ether diverso da zero.

Esploriamo alcune insidie che possono sorgere alla luce delle conoscenze di cui sopra.

Considera il contratto fin troppo semplice,

EtherGame.sol:```solidity contract EtherGame {

root@kitploit:~
uint public payoutMileStone1 = 3 ether;
uint public mileStone1Reward = 2 ether;
uint public payoutMileStone2 = 5 ether;
uint public mileStone2Reward = 3 ether;
uint public finalMileStone = 10 ether;
uint public finalReward = 5 ether;

mapping(address => uint) redeemableEther;
// users pay 0.5 ether. At specific milestones, credit their accounts
function play() public payable {
    require(msg.value == 0.5 ether); // each play is 0.5 ether
    uint currentBalance = this.balance + msg.value;
    // ensure no players after the game as finished
    require(currentBalance <= finalMileStone);
    // if at a milestone credit the players account
    if (currentBalance == payoutMileStone1) {
        redeemableEther[msg.sender] += mileStone1Reward;
    }
    else if (currentBalance == payoutMileStone2) {
        redeemableEther[msg.sender] += mileStone2Reward;
    }
    else if (currentBalance == finalMileStone ) {
        redeemableEther[msg.sender] += finalReward;
    }
    return;
}

function claimReward() public {
    // ensure the game is complete
    require(this.balance == finalMileStone);
    // ensure there is a reward to give
    require(redeemableEther[msg.sender] > 0);
    uint transferValue = redeemableEther[msg.sender];
    redeemableEther[msg.sender] = 0;
    msg.sender.transfer(transferValue);
}

}

root@kitploit:~
Questo contratto rappresenta un semplice gioco (che naturalmente invoca [race-conditions](#race-conditions)) in cui i giocatori inviano quanti di `0.5 ether` al contratto nella speranza di essere il giocatore che raggiunge per primo uno dei tre traguardi. I traguardi sono denominati in ether. Il primo a raggiungere il traguardo può richiedere una parte dell'ether quando il gioco è terminato. Il gioco termina quando viene raggiunto il traguardo finale (`10 ether`) e gli utenti possono richiedere le loro ricompense.

I problemi del contratto `EtherGame` derivano dallo scarso utilizzo di `this.balance` sia nella riga \[14\] (e di conseguenza \[16\]) sia nella riga \[32\]. Un attaccante malintenzionato potrebbe inviare forzatamente una piccola quantità di ether, diciamo `0.1 ether`, tramite la funzione `selfdestruct()` (discussa in precedenza), per impedire a qualsiasi futuro giocatore di raggiungere un traguardo. Poiché tutti i giocatori legittimi possono inviare solo incrementi di `0.5 ether`, `this.balance` non sarebbe più un numero semi-intero, in quanto includerebbe anche il contributo di `0.1 ether`. Questo impedisce che tutte le condizioni if nelle righe \[18\], \[21\] e \[24\] risultino vere.

Ancora peggio, un attaccante vendicativo che ha mancato un traguardo potrebbe inviare forzatamente `10 ether` (o una quantità equivalente di ether che spinga il saldo del contratto oltre `finalMileStone`), bloccando così tutte le ricompense nel contratto per sempre. Questo accade perché la funzione `claimReward()` farebbe sempre revert, a causa della require alla riga \[32\] (cioè `this.balance` è maggiore di `finalMileStone`).

<h3 id="ether-prevention">Tecniche di Prevenzione</h3>

Questa vulnerabilità deriva tipicamente dall'uso improprio di `this.balance`. La logica del contratto, quando possibile, dovrebbe evitare di dipendere da valori esatti del saldo del contratto, perché questo può essere manipolato artificialmente. Se si applica una logica basata su `this.balance`, assicurarsi di tenere conto di saldi imprevisti.

Se sono richiesti valori esatti dell'ether depositato, si dovrebbe usare una variabile definita in proprio che venga incrementata nelle funzioni payable, per tracciare in modo sicuro l'ether depositato. Questa variabile non sarà influenzata dall'ether forzato inviato tramite una chiamata a `selfdestruct()`.

Con questo in mente, una versione corretta del contratto `EtherGame` potrebbe essere la seguente:```solidity
contract EtherGame {

    uint public payoutMileStone1 = 3 ether;
    uint public mileStone1Reward = 2 ether;
    uint public payoutMileStone2 = 5 ether;
    uint public mileStone2Reward = 3 ether;
    uint public finalMileStone = 10 ether;
    uint public finalReward = 5 ether;
    uint public depositedWei;

    mapping (address => uint) redeemableEther;

    function play() public payable {
        require(msg.value == 0.5 ether);
        uint currentBalance = depositedWei + msg.value;
        // ensure no players after the game as finished
        require(currentBalance <= finalMileStone);
        if (currentBalance == payoutMileStone1) {
            redeemableEther[msg.sender] += mileStone1Reward;
        }
        else if (currentBalance == payoutMileStone2) {
            redeemableEther[msg.sender] += mileStone2Reward;
        }
        else if (currentBalance == finalMileStone ) {
            redeemableEther[msg.sender] += finalReward;
        }
        depositedWei += msg.value;
        return;
    }

    function claimReward() public {
        // ensure the game is complete
        require(depositedWei == finalMileStone);
        // ensure there is a reward to give
        require(redeemableEther[msg.sender] > 0);
        uint transferValue = redeemableEther[msg.sender];
        redeemableEther[msg.sender] = 0;
        msg.sender.transfer(transferValue);
    }
 }

Qui, abbiamo appena creato una nuova variabile, depositedWei, che tiene traccia dell'ether noto depositato, ed è su questa variabile che eseguiamo i nostri requisiti e test. Nota che non abbiamo più alcun riferimento a this.balance.

Esempio nel mondo reale: Sconosciuto

Non ho ancora trovato un esempio di questo che sia stato sfruttato in natura. Tuttavia, alcuni esempi di contratti sfruttabili sono stati forniti nell'Underhanded Solidity Contest.

4. Delegatecall

Gli opcode CALL e DELEGATECALL sono utili per consentire agli sviluppatori Ethereum di modularizzare il proprio codice. Le chiamate a messaggi esterni standard ai contratti sono gestite dall'opcode CALL, in cui il codice viene eseguito nel contesto del contratto/funzione esterno. L'opcode DELEGATECALL è identico alla chiamata a messaggio standard, tranne per il fatto che il codice eseguito all'indirizzo target viene eseguito nel contesto del contratto chiamante, e msg.sender e msg.value rimangono invariati. Questa funzionalità consente l'implementazione di librerie con cui gli sviluppatori possono creare codice riutilizzabile per contratti futuri.

Sebbene le differenze tra questi due opcode siano semplici e intuitive, l'uso di DELEGATECALL può portare a un'esecuzione di codice inaspettata.

Per ulteriori letture, consulta Domanda su Ethereum Stack Exchange, Documentazione di Solidity e Come proteggere i tuoi Smart Contract: 6.

La Vulnerabilità

La natura di preservazione del contesto di DELEGATECALL ha dimostrato che creare librerie personalizzate prive di vulnerabilità non è facile come si potrebbe pensare. Il codice nelle librerie stesse può essere sicuro e privo di vulnerabilità, ma quando viene eseguito nel contesto di un'altra applicazione possono sorgere nuove vulnerabilità. Vediamo un esempio abbastanza complesso di questo, utilizzando i numeri di Fibonacci.

Considera la seguente libreria che può generare la sequenza di Fibonacci e sequenze di forma simile. FibonacciLib.sol[^1]```solidity // library contract - calculates fibonacci-like numbers; contract FibonacciLib { // initializing the standard fibonacci sequence; uint public start; uint public calculatedFibNumber;

root@kitploit:~
// modify the zeroth number in the sequence
function setStart(uint _start) public {
    start = _start;
}

function setFibonacci(uint n) public {
    calculatedFibNumber = fibonacci(n);
}

function fibonacci(uint n) internal returns (uint) {
    if (n == 0) return start;
    else if (n == 1) return start + 1;
    else return fibonacci(n - 1) + fibonacci(n - 2);
}

}

root@kitploit:~
Questa libreria fornisce una funzione in grado di generare l'*n*-esimo numero di Fibonacci nella sequenza. Consente agli utenti di modificare il numero iniziale della sequenza (`start`) e calcolare gli *n*-esimi numeri simili a Fibonacci in questa nuova sequenza.

Consideriamo ora un contratto che utilizza questa libreria.


`FibonacciBalance.sol`:```solidity
contract FibonacciBalance {

    address public fibonacciLibrary;
    // the current fibonacci number to withdraw
    uint public calculatedFibNumber;
    // the starting fibonacci sequence number
    uint public start = 3;
    uint public withdrawalCounter;
    // the fibonancci function selector
    bytes4 constant fibSig = bytes4(sha3("setFibonacci(uint256)"));

    // constructor - loads the contract with ether
    constructor(address _fibonacciLibrary) public payable {
        fibonacciLibrary = _fibonacciLibrary;
    }

    function withdraw() {
        withdrawalCounter += 1;
        // calculate the fibonacci number for the current withdrawal user
        // this sets calculatedFibNumber
        require(fibonacciLibrary.delegatecall(fibSig, withdrawalCounter));
        msg.sender.transfer(calculatedFibNumber * 1 ether);
    }

    // allow users to call fibonacci library functions
    function() public {
        require(fibonacciLibrary.delegatecall(msg.data));
    }
}

Questo contratto consente a un partecipante di prelevare ether dal contratto, con l'importo di ether pari al numero di Fibonacci corrispondente all'ordine di prelievo del partecipante; cioè, il primo partecipante riceve 1 ether, il secondo anche 1, il terzo 2, il quarto 3, il quinto 5 e così via (finché il saldo del contratto è inferiore al numero di Fibonacci da prelevare).

Ci sono diversi elementi in questo contratto che potrebbero richiedere qualche spiegazione. Innanzitutto, c'è una variabile dall'aspetto interessante, fibSig. Questa contiene i primi 4 byte dell'hash Keccak (SHA-3) della stringa "setFibonacci(uint256)". Questo è noto come selettore di funzione e viene inserito in calldata per specificare quale funzione di uno smart contract verrà chiamata. Viene usato nella funzione delegatecall alla riga [21] per specificare che vogliamo eseguire la funzione setFibonacci(uint256). Il secondo argomento in delegatecall è il parametro che stiamo passando alla funzione. In secondo luogo, assumiamo che l'indirizzo per la libreria FibonacciLib sia correttamente referenziato nel costruttore (la sezione Riferimento a contratti esterni discute alcune potenziali vulnerabilità relative a questo tipo di inizializzazione di riferimenti a contratti).

Riesci a individuare qualche errore in questo contratto? Se lo inserisci in Remix, lo riempi di ether e chiami withdraw(), probabilmente andrà in revert.

Potresti aver notato che la variabile di stato start è usata sia nella libreria che nel contratto chiamante principale. Nel contratto di libreria, start è usata per specificare l'inizio della sequenza di Fibonacci ed è impostata a 0, mentre è impostata a 3 nel contratto FibonacciBalance. Potresti anche aver notato che la funzione fallback nel contratto FibonacciBalance consente di passare tutte le chiamate al contratto di libreria, il che permette anche di chiamare la funzione setStart() del contratto di libreria. Ricordando che preserviamo lo stato del contratto, potrebbe sembrare che questa funzione permetta di modificare lo stato della variabile start nel contratto locale FibonnacciBalance. Se così fosse, si potrebbe prelevare più ether, poiché il calculatedFibNumber risultante dipende dalla variabile start (come si vede nel contratto di libreria). In realtà, la funzione setStart() non modifica (e non può modificare) la variabile nel contratto . La vulnerabilità sottostante in questo contratto è significativamente peggiore del semplice modificare la variabile .

Prima di discutere il problema reale, facciamo una breve deviazione per capire come le variabili di stato (variabili storage) vengono effettivamente memorizzate nei contratti. Le variabili di stato o storage (variabili che persistono tra le singole transazioni) vengono inserite in slots sequenzialmente, nell'ordine in cui vengono introdotte nel contratto. (Ci sono alcune complessità qui, e invito il lettore a leggere Layout of State Variables in Storage per una comprensione più approfondita).

Ad esempio, osserviamo il contratto di libreria. Ha due variabili di stato, start e calculatedFibNumber. La prima variabile è start, pertanto viene memorizzata nello storage del contratto allo slot[0] (cioè il primo slot). La seconda variabile, calculatedFibNumber, viene inserita nella successiva posizione di storage disponibile, slot[1]. Se osserviamo la funzione setStart(), essa prende un input e imposta start al valore di quell'input. Questa funzione quindi imposta slot[0] a qualunque input forniamo nella funzione setStart(). Analogamente, la funzione setFibonacci() imposta calculatedFibNumber al risultato di fibonacci(n). Anche in questo caso, si tratta semplicemente di impostare lo storage al valore di .

Osserviamo ora il contratto FibonacciBalance. Lo storage slot[0] ora corrisponde all'indirizzo di fibonacciLibrary e slot[1] corrisponde a calculatedFibNumber. È in questa mappatura errata che si verifica la vulnerabilità. delegatecall preserva il contesto del contratto. Ciò significa che il codice eseguito tramite delegatecall agirà sullo stato (cioè lo storage) del contratto chiamante.

Ora nota che in withdraw() alla riga [21] eseguiamo fibonacciLibrary.delegatecall(fibSig,withdrawalCounter). Questo chiama la funzione setFibonacci(), che, come abbiamo discusso, modifica lo storage slot[1], che nel nostro contesto attuale è calculatedFibNumber. Questo è quanto previsto (cioè dopo l'esecuzione, calculatedFibNumber viene aggiornato). Tuttavia, ricorda che la variabile start nel contratto FibonacciLib si trova nello storage slot[0], che è l'indirizzo di fibonacciLibrary nel contratto corrente. Ciò significa che la funzione fibonacci() darà un risultato inaspettato. Questo perché fa riferimento a start (slot[0]), che nel contesto di chiamata attuale è l'indirizzo di (che spesso sarà piuttosto grande, se interpretato come ). Quindi è probabile che la funzione vada in revert, poiché non conterrà una quantità di ether pari a , che è ciò che restituirà.

Ancora peggio, il contratto FibonacciBalance consente agli utenti di chiamare tutte le funzioni di fibonacciLibrary tramite la funzione fallback alla riga [26]. Come abbiamo discusso in precedenza, questo include la funzione setStart(). Abbiamo detto che questa funzione consente a chiunque di modificare o impostare lo storage slot[0]. In questo caso, lo storage slot[0] è l'indirizzo di fibonacciLibrary. Pertanto, un attaccante potrebbe creare un contratto malevolo (un esempio è riportato di seguito), convertire l'indirizzo in un uint (questo può essere fatto facilmente in Python usando int('<address>',16)) e poi chiamare setStart(<attack_contract_address_as_uint>). Questo cambierà fibonacciLibrary nell'indirizzo del contratto di attacco. Poi, ogni volta che un utente chiama withdraw() o la funzione fallback, il contratto malevolo verrà eseguito (e potrà rubare l'intero saldo del contratto) perché abbiamo modificato l'indirizzo effettivo di . Un esempio di tale contratto di attacco sarebbe,```solidity contract Attack { uint storageSlot0; // corresponds to fibonacciLibrary uint storageSlot1; // corresponds to calculatedFibNumber

root@kitploit:~
// fallback - this will run if a specified function is not found
function() public {
    storageSlot1 = 0; // we set calculatedFibNumber to 0, so that if withdraw
    // is called we don't send out any ether.
    <attacker_address>.transfer(this.balance); // we take all the ether
}

}

root@kitploit:~
Notice that this attack contract modifies the `calculatedFibNumber` by changing storage `slot[1]`. In principle, an attacker could modify any other storage slots they choose to perform all kinds of attacks on this contract. I encourage all readers to put these contracts into [Remix](https://remix.ethereum.org) and experiment with different attack contracts and state changes through these `delegatecall` functions.

It is also important to notice that when we say that `delegatecall` is state-preserving, we are not talking about the variable names of the contract, rather the actual storage slots to which those names point. As you can see from this example, a simple mistake, can lead to an attacker hijacking the entire contract and its ether.

<h3 id="dc-prevention">Tecniche di prevenzione</h3>

Solidity fornisce la parola chiave `library` per implementare contratti libreria (vedi la [documentazione di Solidity](http://solidity.readthedocs.io/en/latest/contracts.html?highlight=library#libraries) per ulteriori dettagli). Questo garantisce che il contratto libreria sia stateless e non auto-distruggibile. Forzare le librerie a essere stateless mitiga le complessità del contesto di storage dimostrate in questa sezione. Le librerie stateless impediscono anche attacchi in cui un attaccante modifica direttamente lo stato della libreria per influenzare i contratti che dipendono dal codice della libreria.
Come regola generale, quando si usa `DELEGATECALL` prestare molta attenzione ai possibili contesti di chiamata sia del contratto libreria sia del contratto chiamante e, quando possibile, costruire librerie stateless.

<h3 id="dc-example">Esempio reale: Parity Multisig Wallet (secondo hack)</h3>

Il secondo hack di Parity Multisig Wallet è un esempio di come il contesto di un codice di libreria ben scritto possa essere sfruttato se eseguito in un contesto non previsto. Esistono numerose buone spiegazioni di questo hack, come questa panoramica: [Parity MultiSig Hacked. Again](https://medium.com/chain-cloud-company-blog/parity-multisig-hack-again-b46771eaa838) di Anthony Akentiev, questa [domanda su stack exchange](https://ethereum.stackexchange.com/questions/30128/explanation-of-parity-library-suicide/30130) e [An In-Depth Look at the Parity Multisig Bug](http://hackingdistributed.com/2017/07/22/deep-dive-parity-bug/).

Per aggiungersi a questi riferimenti, esploriamo i contratti che sono stati sfruttati. Il contratto libreria e il contratto wallet possono essere trovati sul github di parity [qui](https://github.com/paritytech/parity/blob/b640df8fbb964da7538eef268dffc125b081a82f/js/src/contracts/snippets/enhanced-wallet.sol).

Guardiamo agli aspetti rilevanti di questo contratto. Ci sono due contratti di interesse contenuti qui, il contratto libreria e il contratto wallet.

Il contratto libreria,```solidity
contract WalletLibrary is WalletEvents {

  ...

  // throw unless the contract is not yet initialized.
  modifier only_uninitialized { if (m_numOwners > 0) throw; _; }

  // constructor - just pass on the owner array to the multiowned and
  // the limit to daylimit
  function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
    initDaylimit(_daylimit);
    initMultiowned(_owners, _required);
  }

  // kills the contract sending everything to `_to`.
  function kill(address _to) onlymanyowners(sha3(msg.data)) external {
    suicide(_to);
  }

  ...

}

e il contratto wallet,```solidity contract Wallet is WalletEvents {

...

// METHODS

// gets called when no other function matches function() payable { // just being sent some cash? if (msg.value > 0) Deposit(msg.sender, msg.value); else if (msg.data.length > 0) _walletLibrary.delegatecall(msg.data); }

...

// FIELDS address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe; }

root@kitploit:~
Nota che il contratto `Wallet` essenzialmente passa tutte le chiamate al contratto `WalletLibrary` tramite una delegate call. L'indirizzo della costante `_walletLibrary` in questo frammento di codice funge da segnaposto per il contratto `WalletLibrary` effettivamente distribuito (che si trovava all'indirizzo `0x863DF6BFa4469f3ead0bE8f9F2AAE51c91A907b4`).

L'operazione prevista di questi contratti era quella di avere un semplice contratto `Wallet` a basso costo di distribuzione, la cui base di codice e funzionalità principale risiedeva nel contratto `WalletLibrary`. Sfortunatamente, il contratto `WalletLibrary` è esso stesso un contratto e mantiene il proprio stato. Riesci a capire perché questo potrebbe essere un problema?

È possibile inviare chiamate al contratto `WalletLibrary` stesso. In particolare, il contratto `WalletLibrary` poteva essere inizializzato e diventare posseduto. Un utente ha fatto questo chiamando la funzione `initWallet()` sul contratto `WalletLibrary`, diventando proprietario del contratto libreria. Lo stesso utente, successivamente, ha chiamato la funzione `kill()`. Poiché l'utente era proprietario del contratto Library, il modificatore ha avuto effetto e il contratto libreria ha eseguito il suicidio. Poiché tutti i contratti `Wallet` esistenti fanno riferimento a questo contratto libreria e non contengono alcun metodo per modificare tale riferimento, tutte le loro funzionalità, inclusa la capacità di ritirare ether, vanno perse insieme al contratto `WalletLibrary`. Più direttamente, tutto l'ether in tutti i portafogli multi-sig Parity di questo tipo diventa istantaneamente perso o permanentemente irrecuperabile.

<h2 id="visibility"><span id="SP-5">5. Visibilità predefinite</span></h2>

Le funzioni in Solidity hanno specificatori di visibilità che determinano come è consentito chiamare le funzioni. La visibilità stabilisce se una funzione può essere chiamata esternamente dagli utenti, da altri contratti derivati, solo internamente o solo esternamente. Esistono quattro specificatori di visibilità, descritti in dettaglio nella [Documentazione di Solidity](http://solidity.readthedocs.io/en/latest/contracts.html?highlight=library#visibility-and-getters). Le funzioni sono `public` per impostazione predefinita, consentendo agli utenti di chiamarle esternamente. Un uso errato degli specificatori di visibilità può portare ad alcune devastanti vulnerabilità negli smart contract, come discusso in questa sezione.

<h3 id="visibility-vuln">La vulnerabilità</h3>

La visibilità predefinita per le funzioni è `public`. Pertanto, le funzioni che non specificano alcuna visibilità saranno chiamabili da utenti esterni. Il problema si presenta quando gli sviluppatori ignorano erroneamente gli specificatori di visibilità su funzioni che dovrebbero essere private (o chiamabili solo all'interno del contratto stesso).

Esploriamo rapidamente un esempio banale.```solidity
contract HashForEther {

    function withdrawWinnings() {
        // Winner if the last 8 hex characters of the address are 0.
        require(uint32(msg.sender) == 0);
        _sendWinnings();
     }

     function _sendWinnings() {
         msg.sender.transfer(this.balance);
     }
}

Questo semplice contratto è progettato per fungere da gioco di caccia al tesoro basato sulla previsione di indirizzi. Per vincere il saldo del contratto, un utente deve generare un indirizzo Ethereum le cui ultime 8 cifre esadecimali siano 0. Una volta ottenuto, può chiamare la funzione WithdrawWinnings() per ottenere la sua ricompensa.

Purtroppo, la visibilità delle funzioni non è stata specificata. In particolare, la funzione _sendWinnings() è public e quindi qualsiasi indirizzo può chiamare questa funzione per rubare la ricompensa.

Tecniche di prevenzione

È buona pratica specificare sempre la visibilità di tutte le funzioni in un contratto, anche se sono intenzionalmente public. Le versioni recenti di Solidity ora mostrano avvisi durante la compilazione per le funzioni che non hanno una visibilità esplicita, per incoraggiare questa pratica.

Esempio reale: Parity MultiSig Wallet (Primo hack)

Nel primo hack del wallet multi-sig di Parity, circa $31M di Ether sono stati rubati principalmente da tre wallet. Un buon riepilogo di come sia successo esattamente è fornito da Haseeb Qureshi in questo post.

In sostanza, il wallet multi-sig (che si può trovare qui) è costruito a partire da un contratto base Wallet che chiama un contratto libreria contenente le funzionalità principali (come descritto in Esempio reale: Parity Multisig (Secondo hack)). Il contratto libreria contiene il codice per inizializzare il wallet, come si può vedere dal seguente snippet```solidity contract WalletLibrary is WalletEvents {

...

// METHODS

...

// constructor is given number of sigs required to do protected "onlymanyowners" transactions // as well as the selection of addresses capable of confirming them. function initMultiowned(address[] _owners, uint _required) { m_numOwners = _owners.length + 1; m_owners[1] = uint(msg.sender); m_ownerIndex[uint(msg.sender)] = 1; for (uint i = 0; i < _owners.length; ++i) { m_owners[2 + i] = uint(_owners[i]); m_ownerIndex[uint(_owners[i])] = 2 + i; } m_required = _required; }

...

// constructor - just pass on the owner array to the multiowned and // the limit to daylimit function initWallet(address[] _owners, uint _required, uint _daylimit) { initDaylimit(_daylimit); initMultiowned(_owners, _required); } }

root@kitploit:~
Nota che nessuna delle due funzioni ha specificato esplicitamente una visibilità. Entrambe le funzioni sono di default `public`. La funzione `initWallet()` viene chiamata nel costruttore dei wallet e imposta i proprietari per il wallet multi-firma, come si può vedere nella funzione `initMultiowned()`. Poiché queste funzioni sono state accidentalmente lasciate `public`, un attaccante è stato in grado di chiamarle sui contratti distribuiti, reimpostando la proprietà all'indirizzo dell'attaccante. Essendo il proprietario, l'attaccante ha poi prosciugato i wallet di tutto il loro ether, per un totale di \$31M.

<h2 id="entropy"><span id="SP-6">6. Illusione dell'Entropia</span></h2>

Tutte le transazioni sulla blockchain di Ethereum sono operazioni deterministiche di transizione di stato. Ciò significa che ogni transazione modifica lo stato globale dell'ecosistema Ethereum e lo fa in modo calcolabile, senza incertezze. Questo implica in definitiva che all'interno dell'ecosistema blockchain non esiste alcuna fonte di entropia o casualità. Non esiste una funzione `rand()` in Solidity. Ottenere entropia decentralizzata (casualità) è un problema ben noto e sono state proposte molte idee per affrontarlo (vedi ad esempio [RandDAO](https://github.com/randao/randao) oppure l'uso di una catena di hash come descritto da Vitalik in questo [post](https://vitalik.ca/files/randomness.html)).

<h3 id="entropy-vuln">La Vulnerabilità</h3>

Alcuni dei primi contratti costruiti sulla piattaforma Ethereum erano basati sul gioco d'azzardo. Fondamentalmente, il gioco d'azzardo richiede incertezza (qualcosa su cui scommettere), il che rende piuttosto difficile costruire un sistema di gioco sulla blockchain (un sistema deterministico). È chiaro che l'incertezza deve provenire da una fonte esterna alla blockchain. Questo è possibile per scommesse tra pari (vedi ad esempio la [tecnica commit-reveal](https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract)), ma è significativamente più difficile se si vuole implementare un contratto che agisca come *il banco* (come nel blackjack o nella roulette). Una trappola comune è l'uso di variabili di blocchi futuri, come hash, timestamp, blocknumber o gas limit. Il problema è che queste sono controllate dal miner che estrae il blocco e pertanto non sono realmente casuali. Considera, ad esempio, un contratto intelligente per la roulette con una logica che restituisce un numero nero se l'hash del blocco successivo termina con un numero pari. Un miner (o un pool di miner) potrebbe scommettere \$1M sul nero. Se risolve il blocco successivo e scopre che l'hash termina con un numero dispari, non pubblicherebbe il proprio blocco e ne minerebbe un altro finché non trova una soluzione con l'hash del blocco che sia un numero pari (supponendo che la ricompensa del blocco e le commissioni siano inferiori a $1M). L'uso di variabili passate o presenti può essere ancora più devastante, come dimostra Martin Swende nel suo eccellente [post sul blog](http://martin.swende.se/blog/Breaking_the_house.html). Inoltre, usare esclusivamente variabili di blocco significa che il numero pseudo-casuale sarà lo stesso per tutte le transazioni in un blocco, quindi un attaccante può moltiplicare le proprie vincite eseguendo molte transazioni all'interno di un blocco (nel caso ci sia una scommessa massima).

<h3 id="entropy-prevention">Tecniche di Prevenzione</h3>

La fonte di entropia (casualità) deve essere esterna alla blockchain. Questo può essere fatto tra pari con sistemi come [commit-reveal](https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract), oppure modificando il modello di fiducia in un gruppo di partecipanti (come in [RandDAO](https://github.com/randao/randao)). Può anche essere fatto tramite un'entità centralizzata, che agisce come un oracolo di casualità. Le variabili di blocco (in generale, ci sono alcune eccezioni) non dovrebbero essere utilizzate come fonte di entropia perché possono essere manipolate dai miner.

<h3 id="entropy-example">Esempio Reale: Contratti PRNG</h3>

Arseny Reutov ha scritto un [post sul blog](https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620) dopo aver analizzato 3649 contratti intelligenti attivi che utilizzavano una qualche forma di generatore di numeri pseudo-casuali (PRNG) e ha trovato 43 contratti che potevano essere sfruttati.

<h2 id="contract-reference"><span id="SP-7">7. Riferimento a Contratti Esterni</span></h2>

Uno dei vantaggi del *computer globale* di Ethereum è la possibilità di riutilizzare il codice e interagire con contratti già distribuiti sulla rete. Di conseguenza, un gran numero di contratti fa riferimento a contratti esterni e, in generale, usa chiamate di messaggi esterni per interagire con questi contratti. Queste chiamate di messaggi esterni possono nascondere le intenzioni di attori malintenzionati in modi non ovvi, come discuteremo.

<h3 id="cr-vuln">La Vulnerabilità</h3>

In Solidity, qualsiasi indirizzo può essere convertito in un contratto, indipendentemente dal fatto che il codice all'indirizzo rappresenti il tipo di contratto che si sta convertendo. Questo può essere ingannevole, soprattutto quando l'autore del contratto cerca di nascondere codice dannoso. Illustriamo questo con un esempio:

Considera un frammento di codice che implementa rudimentalmente la cifratura [Rot13](https://github.com/al1ex/soliditysecurity/blob/HEAD/www.wikipedia.com/rot13).

`Rot13Encryption.sol`:```solidity
//encryption contract
contract Rot13Encryption {

   event Result(string convertedString);

    //rot13 encrypt a string
    function rot13Encrypt (string text) public {
        uint256 length = bytes(text).length;
        for (var i = 0; i < length; i++) {
            byte char = bytes(text)[i];
            //inline assembly to modify the string
            assembly {
                char := byte(0,char) // get the first byte
                if and(gt(char,0x6D), lt(char,0x7B)) // if the character is in [n,z], i.e. wrapping.
                { char:= sub(0x60, sub(0x7A,char)) } // subtract from the ascii number a by the difference char is from z.
                if iszero(eq(char, 0x20)) // ignore spaces
                {mstore8(add(add(text,0x20), mul(i,1)), add(char,13))} // add 13 to char.
            }
        }
        emit Result(text);
    }

    // rot13 decrypt a string
    function rot13Decrypt (string text) public {
        uint256 length = bytes(text).length;
        for (var i = 0; i < length; i++) {
            byte char = bytes(text)[i];
            assembly {
                char := byte(0,char)
                if and(gt(char,0x60), lt(char,0x6E))
                { char:= add(0x7B, sub(char,0x61)) }
                if iszero(eq(char, 0x20))
                {mstore8(add(add(text,0x20), mul(i,1)), sub(char,13))}
            }
        }
        emit Result(text);
    }
}

Questo codice semplicemente prende una stringa (lettere a-z, senza validazione) e la cifra spostando ogni carattere di 13 posizioni a destra (avvolgendo attorno a 'z'); cioè 'a' diventa 'n' e 'x' diventa 'k'. L'assembly qui non è importante, quindi non preoccuparti se non ha senso in questa fase.

Considera il seguente contratto che usa questo codice per la sua cifratura,```solidity import "Rot13Encryption.sol";

// encrypt your top secret info contract EncryptionContract { // library for encryption Rot13Encryption encryptionLibrary;

root@kitploit:~
// constructor - initialise the library
constructor(Rot13Encryption _encryptionLibrary) {
    encryptionLibrary = _encryptionLibrary;
}

function encryptPrivateData(string privateInfo) {
    // potentially do some operations here
    encryptionLibrary.rot13Encrypt(privateInfo);
 }

}

root@kitploit:~
Il problema con questo contratto è che l'indirizzo `encryptionLibrary` non è pubblico o costante. Pertanto il deployer del contratto potrebbe aver fornito un indirizzo nel costruttore che punta a questo contratto:```solidity
//encryption contract
contract Rot26Encryption {

   event Result(string convertedString);

    //rot13 encrypt a string
    function rot13Encrypt (string text) public {
        uint256 length = bytes(text).length;
        for (var i = 0; i < length; i++) {
            byte char = bytes(text)[i];
            //inline assembly to modify the string
            assembly {
                char := byte(0,char) // get the first byte
                if and(gt(char,0x6D), lt(char,0x7B)) // if the character is in [n,z], i.e. wrapping.
                { char:= sub(0x60, sub(0x7A,char)) } // subtract from the ascii number a by the difference char is from z.
                if iszero(eq(char, 0x20)) // ignore spaces
                {mstore8(add(add(text,0x20), mul(i,1)), add(char,26))} // add 13 to char.
            }
        }
        emit Result(text);
    }

    // rot13 decrypt a string
    function rot13Decrypt (string text) public {
        uint256 length = bytes(text).length;
        for (var i = 0; i < length; i++) {
            byte char = bytes(text)[i];
            assembly {
                char := byte(0,char)
                if and(gt(char,0x60), lt(char,0x6E))
                { char:= add(0x7B, sub(char,0x61)) }
                if iszero(eq(char, 0x20))
                {mstore8(add(add(text,0x20), mul(i,1)), sub(char,26))}
            }
        }
        emit Result(text);
    }
}

che implementa la cifratura rot26 (sposta ogni carattere di 26 posizioni, capito? :p). Anche in questo caso, non c'è bisogno di comprendere l'assembly in questo contratto. Il deployer avrebbe potuto anche collegare il seguente contratto:```solidity contract Print{ event Print(string text);

root@kitploit:~
function rot13Encrypt(string text) public {
    emit Print(text);
}

}

root@kitploit:~
Se l'indirizzo di uno di questi contratti fosse fornito nel costruttore, la funzione `encryptPrivateData()` produrrebbe semplicemente un evento che stampa i dati privati non cifrati. Sebbene in questo esempio un contratto simile a una libreria sia stato impostato nel costruttore, accade spesso che un utente privilegiato (come un `owner`) possa modificare gli indirizzi dei contratti libreria. Se un contratto collegato non contiene la funzione chiamata, verrà eseguita la funzione di fallback. Ad esempio, con la riga `encryptionLibrary.rot13Encrypt()`, se il contratto specificato da `encryptionLibrary` fosse:```solidity
 contract Blank {
     event Print(string text);
     function () {
         emit Print("Here");
         //put malicious code here and it will run
     }
 }

then an event with the text "Here" would be emitted. Thus if users can alter contract libraries, they can in principle get users to unknowingly run arbitrary code.

Nota: non utilizzare contratti di crittografia come questi, poiché i parametri di input dei contratti intelligenti sono visibili sulla blockchain. Inoltre il cifrario Rot non è una tecnica di crittografia consigliata :p

Tecniche di prevenzione

Come dimostrato sopra, contratti senza vulnerabilità possono (in alcuni casi) essere distribuiti in modo tale da comportarsi in maniera dannosa. Un revisore potrebbe verificare pubblicamente un contratto e far sì che il suo proprietario lo distribuisca in modo malintenzionato, dando vita a un contratto pubblicamente revisionato che presenta vulnerabilità o intenti dannosi.

Esistono diverse tecniche che prevengono questi scenari.

Una tecnica è quella di usare la parola chiave new per creare contratti. Nell'esempio precedente, il costruttore potrebbe essere scritto come:```solidity constructor() { encryptionLibrary = new Rot13Encryption(); }

root@kitploit:~
In questo modo un'istanza del contratto referenziato viene creata al momento del deployment e il deployer non può sostituire il contratto `Rot13Encryption` con nient'altro senza modificare lo smart contract.

Un'altra soluzione è hard codare gli indirizzi dei contratti esterni, se noti.

In generale, il codice che chiama contratti esterni dovrebbe essere sempre esaminato con attenzione. Come sviluppatore, quando si definiscono contratti esterni, può essere una buona idea rendere pubblici gli indirizzi dei contratti (cosa che non accade nell'esempio di honey pot riportato di seguito) per consentire agli utenti di esaminare facilmente quale codice viene referenziato dal contratto. Al contrario, se un contratto ha una variabile privata per l'indirizzo del contratto, può essere un segno di comportamento malevolo (come mostrato nell'esempio del mondo reale). Se un utente privilegiato (o qualsiasi utente) è in grado di modificare un indirizzo di contratto utilizzato per chiamare funzioni esterne, può essere importante (in un contesto di sistema decentralizzato) implementare un meccanismo di time-lock o di voto per consentire agli utenti di vedere quale codice viene modificato o per dare ai partecipanti la possibilità di aderire o uscire con il nuovo indirizzo del contratto.


<h3 id="cr-example">Esempio Reale: Re-Entrancy Honey Pot</h3>

Un certo numero di recenti honey pot sono stati rilasciati sulla mainnet. Questi contratti cercano di superare in astuzia gli hacker di Ethereum che tentano di sfruttare i contratti, ma che a loro volta finiscono per perdere ether a causa del contratto che si aspettano di sfruttare. Un esempio impiega l'attacco sopra descritto sostituendo un contratto previsto con uno malizioso nel costruttore. Il codice si trova [qui](https://etherscan.io/address/0x95d34980095380851902ccd9a1fb4c813c2cb639#code):```solidity
pragma solidity ^0.4.19;

contract Private_Bank
{
    mapping (address => uint) public balances;
    uint public MinDeposit = 1 ether;
    Log TransferLog;

    function Private_Bank(address _log)
    {
        TransferLog = Log(_log);
    }

    function Deposit()
    public
    payable
    {
        if(msg.value >= MinDeposit)
        {
            balances[msg.sender]+=msg.value;
            TransferLog.AddMessage(msg.sender,msg.value,"Deposit");
        }
    }

    function CashOut(uint _am)
    {
        if(_am<=balances[msg.sender])
        {
            if(msg.sender.call.value(_am)())
            {
                balances[msg.sender]-=_am;
                TransferLog.AddMessage(msg.sender,_am,"CashOut");
            }
        }
    }

    function() public payable{}

}

contract Log
{
    struct Message
    {
        address Sender;
        string  Data;
        uint Val;
        uint  Time;
    }

    Message[] public History;
    Message LastMsg;

    function AddMessage(address _adr,uint _val,string _data)
    public
    {
        LastMsg.Sender = _adr;
        LastMsg.Time = now;
        LastMsg.Val = _val;
        LastMsg.Data = _data;
        History.push(LastMsg);
    }
}

Questo post di un utente di reddit spiega come hanno perso 1 ether con questo contratto cercando di sfruttare il bug di rientranza che si aspettavano fosse presente nel contratto.

8. Attacco da indirizzo/parametro corto

Questo attacco non viene eseguito specificamente sui contratti Solidity stessi, ma su applicazioni di terze parti che possono interagire con essi. Aggiungo questo attacco per completezza e per essere consapevoli di come i parametri possano essere manipolati nei contratti.

Per ulteriori letture, vedi The ERC20 Short Address Attack Explained, ICO Smart contract Vulnerability: Short Address Attack o questo post su reddit.

La Vulnerabilità

Quando si passano parametri a uno smart contract, i parametri vengono codificati secondo la specifica ABI. È possibile inviare parametri codificati più corti della lunghezza prevista (ad esempio, inviando un indirizzo di soli 38 caratteri esadecimali (19 byte) invece dei 40 caratteri esadecimali (20 byte) standard). In tale scenario, l'EVM aggiungerà zeri (0) alla fine dei parametri codificati per raggiungere la lunghezza prevista.

Questo diventa un problema quando le applicazioni di terze parti non convalidano gli input. L'esempio più chiaro è un exchange che non verifica l'indirizzo di un token ERC20 quando un utente richiede un prelievo. Questo esempio è trattato in maggior dettaglio nel post di Peter Venesses, The ERC20 Short Address Attack Explained menzionato sopra.

Considera l'interfaccia della funzione di trasferimento standard ERC20, notando l'ordine dei parametri,```solidity function transfer(address to, uint tokens) public returns (bool success);

root@kitploit:~
Now consider, un exchange che detiene una grande quantità di un token (ad esempio `REP`) e un utente che desidera ritirare la propria quota di 100 token. L'utente invierebbe il proprio indirizzo, `0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead`, e il numero di token, `100`. L'exchange codificherebbe questi parametri nell'ordine specificato dalla funzione `transfer()`, cioè `address` e poi `tokens`. Il risultato codificato sarebbe `a9059cbb000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddeaddead0000000000000` `000000000000000000000000000000000056bc75e2d63100000`. I primi quattro byte (`a9059cbb`) sono il [selettore/firma di funzione](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) di `transfer()`, i successivi 32 byte sono l'indirizzo, seguiti dagli ultimi 32 byte che rappresentano il numero `uint256` di token. Si noti che l'esadecimale `56bc75e2d63100000` alla fine corrisponde a 100 token (con 18 decimali, come specificato dal contratto del token `REP`).

Ok, quindi ora vediamo cosa succede se inviamo un indirizzo a cui manca 1 byte (2 cifre esadecimali). Nello specifico, supponiamo che un attaccante invii `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde` come indirizzo (mancano le ultime due cifre) e gli stessi `100` token da ritirare. Se l'exchange non valida questo input, verrebbe codificato come `a9059cbb000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddeadde00000000000000` `00000000000000000000000000000000056bc75e2d6310000000`. La differenza è sottile. Notare che `00` è stato aggiunto in coda alla codifica, per compensare l'indirizzo corto inviato. Quando questo viene inviato allo smart contract, i parametri `address` verranno letti come `0xdeaddeaddeaddeaddeaddeaddeaddeaddeadde00` e il valore verrà letto come `56bc75e2d6310000000` (si notino i due `0` extra). Questo valore ora corrisponde a `25600` token (il valore è stato moltiplicato per `256`). In questo esempio, se l'exchange detenesse così tanti token, l'utente ritirerebbe `25600` token (mentre l'exchange pensa che l'utente ne stia ritirando solo `100`) all'indirizzo modificato. Ovviamente in questo esempio l'attaccante non possiederà l'indirizzo modificato, ma se l'attaccante generasse un qualsiasi indirizzo che termina con `0` (che può essere facilmente forzato brute-force) e usasse questo indirizzo generato, potrebbe facilmente rubare token all'exchange ignaro.

<h3 id="short-prev">Tecniche di prevenzione</h3>

Suppongo che sia ovvio affermare che validare tutti gli input prima di inviarli alla blockchain prevenga questo tipo di attacchi. Va inoltre notato che l'ordinamento dei parametri gioca un ruolo importante qui. Poiché il padding avviene solo alla fine, un attento ordinamento dei parametri nello smart contract può potenzialmente mitigare alcune forme di questo attacco.

<h3 id="short-example">Esempio reale: sconosciuto</h3>

Non sono a conoscenza di alcun attacco pubblicizzato di questo tipo.

<h2 id="unchecked-calls"><span id="SP-9">9. Valori di ritorno di CALL non controllati</span></h2>

Esistono diversi modi per effettuare chiamate esterne in Solidity. L'invio di ether a conti esterni viene comunemente eseguito tramite il metodo `transfer()`. Tuttavia, si può usare anche la funzione `send()` e, per chiamate esterne più versatili, l'opcode `CALL` può essere impiegato direttamente in Solidity. Le funzioni `call()` e `send()` restituiscono un booleano che indica se la chiamata è riuscita o fallita. Queste funzioni hanno quindi una semplice avvertenza: la transazione che esegue queste funzioni non verrà annullata (non farà revert) se la chiamata esterna (inizializzata da `call()` o `send()`) fallisce; piuttosto, `call()` o `send()` restituiranno semplicemente `false`. Una trappola comune si verifica quando il valore di ritorno non viene controllato, e lo sviluppatore si aspetta invece che avvenga un revert.

Per ulteriori letture, consultare [DASP Top 10](http://www.dasp.co/#item-4) e [Scansione dei contratti Ethereum live per il bug "Unchecked-Send"](http://hackingdistributed.com/2016/06/16/scanning-live-ethereum-contracts-for-bugs/).

<h3 id="unchecked-calls-vuln">La vulnerabilità</h3>

Considera il seguente esempio:```solidity
contract Lotto {

    bool public payedOut = false;
    address public winner;
    uint public winAmount;

    // ... extra functionality here

    function sendToWinner() public {
        require(!payedOut);
        winner.send(winAmount);
        payedOut = true;
    }

    function withdrawLeftOver() public {
        require(payedOut);
        msg.sender.send(this.balance);
    }
}

Questo contratto rappresenta un contratto simile a una lotteria, in cui un winner riceve winAmount di ether, che tipicamente lascia un piccolo resto che chiunque può prelevare.

Il bug si trova alla riga [11], dove viene usato send() senza controllare la risposta. In questo esempio banale, un winner la cui transazione fallisce (o per esaurimento del gas o perché è un contratto che lancia intenzionalmente un'eccezione nella funzione fallback) consente che payedOut venga impostato a true (indipendentemente dal fatto che l'ether sia stato inviato o meno). In questo caso, il pubblico può prelevare le vincite del winner tramite la funzione withdrawLeftOver().

Tecniche di prevenzione

Quando possibile, usa la funzione transfer() invece di send(), poiché transfer() esegue revert se la transazione esterna viene annullata. Se è necessario send(), assicurati sempre di controllare il valore di ritorno.

Una raccomandazione ancora più robusta è adottare un modello di prelievo. In questa soluzione, ogni utente ha l'onere di chiamare una funzione isolata (cioè una funzione di prelievo) che gestisce l'invio di ether fuori dal contratto e quindi gestisce in modo indipendente le conseguenze delle transazioni di invio fallite. L'idea è isolare logicamente la funzionalità di invio esterno dal resto del codebase e far ricadere l'onere di una potenziale transazione fallita sull'utente finale che chiama la funzione di prelievo.

Esempio reale: Etherpot e King of the Ether

Etherpot era una lotteria basata su smart contract, non troppo dissimile dal contratto d'esempio menzionato sopra. Il codice Solidity di Etherpot si trova qui: lotto.sol. Il difetto principale di questo contratto era dovuto a un uso errato degli hash dei blocchi (solo gli ultimi 256 hash di blocchi sono utilizzabili, vedi il post di Aakil Fernandes su come Etherpot non sia riuscito a implementarlo correttamente). Tuttavia, questo contratto soffriva anche di un valore di chiamata non controllato. Si noti la funzione cash() alla riga [80] di lotto.sol:```solidity ... function cash(uint roundIndex, uint subpotIndex){

root@kitploit:~
    var subpotsCount = getSubpotsCount(roundIndex);

    if(subpotIndex>=subpotsCount)
        return;

    var decisionBlockNumber = getDecisionBlockNumber(roundIndex,subpotIndex);

    if(decisionBlockNumber>block.number)
        return;

    if(rounds[roundIndex].isCashed[subpotIndex])
        return;
    //Subpots can only be cashed once. This is to prevent double payouts

    var winner = calculateWinner(roundIndex,subpotIndex);
    var subpot = getSubpot(roundIndex);

    winner.send(subpot);

    rounds[roundIndex].isCashed[subpotIndex] = true;
    //Mark the round as cashed

} ...

root@kitploit:~
Nota che alla riga \[21\] il valore di ritorno della funzione `send` non viene controllato, e la riga successiva imposta un booleano che indica che al vincitore sono stati inviati i fondi. Questo bug può consentire uno stato in cui il vincitore non riceve i suoi ether, ma lo stato del contratto può indicare che il vincitore è già stato pagato.

Una versione più grave di questo bug si è verificata in [King of the Ether](https://www.kingoftheether.com/thrones/kingoftheether/index.html). È stato scritto un eccellente [post-mortem](https://www.kingoftheether.com/postmortem.html) di questo contratto che descrive in dettaglio come un `send()` fallito e non controllato potesse essere usato per attaccare il contratto.


<h2 id="race-conditions"><span id="SP-10">10. Condizioni di gara / Front Running</span></h2>

La combinazione di chiamate esterne ad altri contratti e la natura multi-utente della blockchain sottostante dà origine a una varietà di potenziali insidie di Solidity in cui gli utenti *gareggiano* nell'esecuzione del codice per ottenere stati inattesi. [Re-Entrancy](#reentrancy) è un esempio di una tale condizione di gara. In questa sezione parleremo più in generale dei diversi tipi di condizione di gara che possono verificarsi sulla blockchain di Ethereum. Esiste una varietà di buoni articoli su questo argomento, alcuni dei quali sono: [Ethereum Wiki - Safety](https://github.com/ethereum/wiki/wiki/Safety#race-conditions), [DASP - Front-Running](http://www.dasp.co/#item-7) e [Consensus - Smart Contract Best Practices](https://consensys.github.io/smart-contract-best-practices/known_attacks/#race-conditions).

<h3 id="race-conditions-vuln">La vulnerabilità</h3>

Come per la maggior parte delle blockchain, i nodi Ethereum mettono in pool le transazioni e le raggruppano in blocchi. Le transazioni sono considerate valide solo dopo che un miner ha risolto un meccanismo di consenso (attualmente [ETHASH](https://github.com/ethereum/wiki/wiki/Ethash) PoW per Ethereum). Il miner che risolve il blocco sceglie anche quali transazioni dal pool saranno incluse nel blocco; in genere sono ordinate in base al `gasPrice` di una transazione. Qui risiede un potenziale vettore di attacco. Un attaccante può osservare il pool di transazioni alla ricerca di transazioni che possano contenere soluzioni a problemi, modificare o revocare i permessi dell'attaccante o cambiare uno stato in un contratto che è indesiderabile per l'attaccante. L'attaccante può quindi ottenere i dati di questa transazione e creare una propria transazione con un `gasPrice` più alto e far includere la propria transazione in un blocco prima di quella originale.

Vediamo come potrebbe funzionare con un semplice esempio. Considera il contratto `FindThisHash.sol`:```solidity
contract FindThisHash {
    bytes32 constant public hash = 0xb5b5b97fafd9855eec9b41f74dfb6c38f5951141f9a3ecd7f44d5479b630ee0a;

    constructor() public payable {} // load with ether

    function solve(string solution) public {
        // If you can find the pre image of the hash, receive 1000 ether
        require(hash == sha3(solution));
        msg.sender.transfer(1000 ether);
    }
}

Immagina che questo contratto contenga 1000 ether. L'utente che riesce a trovare la preimmagine dell'hash sha3 0xb5b5b97fafd9855eec9b41f74dfb6c38f5951141f9a3ecd7f44d5479b630ee0a può inviare la soluzione e recuperare i 1000 ether. Diciamo che un utente scopre che la soluzione è Ethereum!. Chiama solve() con Ethereum! come parametro. Sfortunatamente un attaccante è stato abbastanza astuto da osservare il pool di transazioni per chiunque invii una soluzione. Vedono questa soluzione, ne verificano la validità, e poi inviano una transazione equivalente con un gasPrice molto più alto rispetto alla transazione originale. Il miner che risolve il blocco probabilmente darà la preferenza all'attaccante a causa del gasPrice più alto e accetterà la sua transazione prima di quella del risolutore originale. L'attaccante prenderà i 1000 ether e l'utente che ha risolto il problema non otterrà nulla (non rimane ether nel contratto).

Un problema più realistico si presenta nella progettazione della futura implementazione di Casper. I contratti di proof of stake di Casper invocano condizioni di slashing in cui gli utenti che notano validatori che votano due volte o si comportano male sono incentivati a inviare una prova che lo hanno fatto. Il validatore verrà punito e l'utente premiato. In uno scenario del genere, ci si aspetta che miner e utenti facciano front-running su tutte queste presentazioni di prove, e questo problema deve essere affrontato prima del rilascio finale.

Tecniche di prevenzione

Ci sono due classi di utenti che possono eseguire questi tipi di attacchi di front-running. Gli utenti (che modificano il gasPrice delle loro transazioni) e i miner stessi (che possono riordinare le transazioni in un blocco come meglio credono). Un contratto vulnerabile alla prima classe (gli utenti) è in una situazione significativamente peggiore rispetto a uno vulnerabile alla seconda (i miner), poiché i miner possono eseguire l'attacco solo quando risolvono un blocco, il che è improbabile per un singolo miner che prende di mira un blocco specifico. Qui elencherò alcune misure di mitigazione in relazione alla classe di attaccanti che possono prevenire.

Un metodo che può essere impiegato è creare una logica nel contratto che pone un limite superiore al gasPrice. Questo impedisce agli utenti di aumentare il gasPrice e ottenere un ordinamento preferenziale delle transazioni oltre il limite superiore. Questa misura preventiva mitiga solo la prima classe di attaccanti (utenti arbitrari). In questo scenario, i miner possono ancora attaccare il contratto poiché possono ordinare le transazioni nel loro blocco come preferiscono, indipendentemente dal prezzo del gas.

Un metodo più robusto consiste nell'usare uno schema commit-reveal, quando possibile. Tale schema prevede che gli utenti inviino transazioni con informazioni nascoste (tipicamente un hash). Dopo che la transazione è stata inclusa in un blocco, l'utente invia una transazione che rivela i dati inviati (la fase di reveal). Questo metodo impedisce sia ai miner che agli utenti di fare front-running sulle transazioni, poiché non possono determinare il contenuto della transazione. Tuttavia, questo metodo non può nascondere il valore della transazione (che in alcuni casi è l'informazione preziosa che deve essere nascosta). Lo smart contract ENS consentiva agli utenti di inviare transazioni i cui dati impegnati includevano l'importo di ether che erano disposti a spendere. Gli utenti potevano quindi inviare transazioni di valore arbitrario. Durante la fase di reveal, agli utenti veniva rimborsata la differenza tra l'importo inviato nella transazione e l'importo che erano disposti a spendere.

Un ulteriore suggerimento di Lorenz, Phil, Ari e Florian è di usare Submarine Sends. Un'implementazione efficiente di questa idea richiede l'opcode CREATE2, che attualmente non è stato adottato, ma sembra probabile nei prossimi hard fork.

Esempi del mondo reale: ERC20 e Bancor

Lo standard ERC20 è piuttosto noto per la creazione di token su Ethereum. Questo standard ha una potenziale vulnerabilità di front-running che deriva dalla funzione approve(). Una buona spiegazione di questa vulnerabilità si trova qui.

Lo standard specifica la funzione approve() come:```solidity function approve(address _spender, uint256 _value) returns (bool success)

root@kitploit:~
Questa funzione consente a un utente di permettere ad altri utenti di trasferire token per suo conto. La vulnerabilità di frontrunning si presenta nello scenario in cui un utente, Alice, *approva* il suo amico `Bob` a spendere `100 token`. Alice in seguito decide di voler revocare l'approvazione di `Bob` a spendere `100 token`, quindi crea una transazione che imposta l'allocazione di `Bob` a `50 token`. `Bob`, che ha osservato attentamente la catena, vede questa transazione e costruisce una propria transazione per spendere i `100 token`. Mette un `gasPrice` più alto sulla sua transazione rispetto a quello di `Alice` e fa sì che la sua transazione venga prioritizzata rispetto a quella di lei. Alcune implementazioni di `approve()` permetterebbero a `Bob` di trasferire i suoi `100 token`, poi, quando la transazione di `Alice` viene committata, reimpostano l'approvazione di `Bob` a `50 token`, di fatto dando a `Bob` accesso a `150 token`. Le strategie di mitigazione di questo attacco sono fornite [qui](https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit) nel documento collegato sopra.

Un altro esempio importante e reale è [Bancor](https://www.bancor.network/). Ivan Bogatty e il suo team hanno documentato un attacco redditizio sull'implementazione iniziale di Bancor. Il suo [post sul blog](https://hackernoon.com/front-running-bancor-in-150-lines-of-python-with-ethereum-api-d5e2bfd0d798) e il [talk Devon 3](https://www.youtube.com/watch?v=RL2nE3huNiI) discutono in dettaglio come è stato fatto. In sostanza, i prezzi dei token sono determinati in base al valore della transazione; gli utenti possono osservare il pool di transazioni per le transazioni Bancor ed eseguire front running su di esse per trarre profitto dalle differenze di prezzo. Questo attacco è stato affrontato dal team di Bancor.

<h2 id="dos"><span id="SP-11">11. Denial of Service (DOS)</span></h2>

Questa categoria è molto ampia, ma fondamentalmente consiste in attacchi in cui gli utenti possono rendere il contratto inoperabile per un breve periodo di tempo o, in alcuni casi, permanentemente. Questo può intrappolare ether in questi contratti per sempre, come è stato il caso con il [secondo hack di Parity MultiSig](#dc-example)

<h3 id="dos-vuln">La Vulnerabilità</h3>

Ci sono vari modi in cui un contratto può diventare inoperabile. Qui evidenzierò solo alcuni pattern di codifica Solidity della Blockchain, potenzialmente meno ovvi, che possono portare gli attaccanti a eseguire attacchi DOS.

**1. Chiamate esterne senza stipendi di gas** - Potrebbe essere il caso che tu voglia
fare una chiamata esterna a un contratto sconosciuto e continuare a elaborare la
transazione indipendentemente dal fatto che la chiamata fallisca o meno. Tipicamente questo è
ottenuto usando l'opcode `CALL`, che non esegue il revert della transazione
se la chiamata fallisce (vedi [Valori di ritorno CALL non controllati](#unchecked-calls) per ulteriori dettagli ed esempi).
Consideriamo un semplice esempio, in cui abbiamo un portafoglio contrattuale che lentamente
eroga ether quando viene chiamata la funzione `withdraw()`. Un `partner` può
aggiungere il proprio indirizzo e spendere gas per chiamare il prelievo, dando sia al
`partner` che all'`owner` l'1% del saldo totale del contratto.```solidity
contract TrickleWallet {

    address public partner; // withdrawal partner - pay the gas, split the withdraw
    address public constant owner = 0xA9E;
    uint timeLastWithdrawn;
    mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances

    function setWithdrawPartner(address _partner) public {
        require(partner == '0x0' || msg.sender == partner);
        partner = _partner;
    }

    // withdraw 1% to recipient and 1% to owner
    function withdraw() public {
        uint amountToSend = address(this).balance/100;
        // perform a call without checking return
        // the recipient can revert, the owner will still get their share
        partner.call.value(amountToSend)();
        owner.transfer(amountToSend);
        // keep track of last withdrawal time
        timeLastWithdrawn = now;
        withdrawPartnerBalances[partner] += amountToSend;
    }

    // allow deposit of funds
    function() payable {}

    // convenience function
    function contractBalance() view returns (uint) {
        return address(this).balance;
    }
}

Nota che alla riga [17] effettuiamo una chiamata esterna che invia l'1% del saldo del contratto a un account specificato dall'utente. Il motivo per cui viene usato l'opcode CALL è garantire che il proprietario venga comunque pagato, anche se la chiamata esterna va in revert. Il problema è che la transazione invierà tutto il suo gas (in realtà, solo la maggior parte del gas della transazione viene inviata, un po' ne rimane per terminare l'elaborazione della chiamata) alla chiamata esterna. Se l'utente fosse malintenzionato, potrebbe creare un contratto che consumi tutto il gas e costringere tutte le transazioni a withdraw() a fallire, a causa dell'esaurimento del gas.

Ad esempio, considera il seguente contratto malintenzionato che consuma tutto il gas,```solidity contract ConsumeAllGas { function () payable { // an assert consumes all transaction gas, unlike a //revert which returns the remaining gas assert(1==2); } }

root@kitploit:~
Se un partner di prelievo decidesse che non gradisce il proprietario del contratto.
Potrebbe impostare l'indirizzo del partner su questo contratto e bloccare tutti i fondi in
il contratto `TrickleWallet` per sempre.

Per prevenire tali vettori di attacco DoS, assicurati che venga specificato uno stipendio di gas in una
chiamata esterna, per limitare la quantità di gas che quella transazione può utilizzare. Nel nostro
esempio, potremmo rimediare a questo attacco modificando la riga \[17\] in:```solidity
partner.call.gas(50000).value(amountToSend)();

Questa modifica consente di spendere solo 50,000 gas per la transazione esterna. Il owner può impostare un prezzo del gas superiore a questo, affinché la propria transazione venga completata, indipendentemente da quanto la transazione esterna utilizzi.

2. Iterare attraverso mapping o array manipolati esternamente - Nelle mie esperienze ho visto varie forme di questo tipo di pattern. Tipicamente appare in scenari in cui un owner desidera distribuire token tra i propri investitori, e lo fa con una funzione simile a distribute(), come si può vedere nel contratto di esempio:```solidity contract DistributeTokens { address public owner; // gets set somewhere address[] investors; // array of investors uint[] investorTokens; // the amount of tokens each investor gets

root@kitploit:~
// ... extra functionality, including transfertoken()

function invest() public payable {
    investors.push(msg.sender);
    investorTokens.push(msg.value * 5); // 5 times the wei sent
    }

function distribute() public {
    require(msg.sender == owner); // only owner
    for(uint i = 0; i < investors.length; i++) {
        // here transferToken(to,amount) transfers "amount" of tokens to the address "to"
        transferToken(investors[i],investorTokens[i]);
    }
}

}

root@kitploit:~
Si noti che il ciclo in questo contratto opera su un array che può essere gonfiato artificialmente. Un attaccante può creare molti account utente rendendo l'array `investor` di grandi dimensioni. In linea di principio, ciò può essere fatto in modo che il gas richiesto per eseguire il ciclo for superi il limite di gas del blocco, rendendo di fatto inutilizzabile la funzione `distribute()`.

**3. Operazioni del proprietario** - Un altro modello comune è quello in cui i proprietari hanno privilegi specifici nei contratti e devono eseguire un compito affinché il contratto proceda allo stato successivo. Un esempio potrebbe essere un contratto ICO che richiede al proprietario di `finalize()` il contratto, il che successivamente consente ai token di essere trasferibili, cioè``` solidity
bool public isFinalized = false;
address public owner; // gets set somewhere

function finalize() public {
    require(msg.sender == owner);
    isFinalized == true;
}

// ... extra ICO functionality

// overloaded transfer function
function transfer(address _to, uint _value) returns (bool) {
    require(isFinalized);
    super.transfer(_to,_value)
}

...

In tali casi, se un utente privilegiato perde le proprie chiavi private o diventa inattivo, l'intero contratto di token diventa inoperabile. In questo caso, se l'owner non può chiamare finalize(), nessun token può essere trasferito; cioè l'intera operatività dell'ecosistema di token dipende da un singolo indirizzo.

4. Avanzamento dello stato basato su chiamate esterne - I contratti sono talvolta scritti in modo tale che, per avanzare a un nuovo stato, sia necessario inviare ether a un indirizzo o attendere un input da una fonte esterna. Questi pattern possono portare ad attacchi DoS quando la chiamata esterna fallisce o viene impedita per ragioni esterne. Nell'esempio dell'invio di ether, un utente può creare un contratto che non accetta ether. Se un contratto richiede che gli ether vengano prelevati (si consideri un contratto con timelock che richiede che tutti gli ether siano prelevati prima di poter essere nuovamente utilizzabile) per avanzare a un nuovo stato, il contratto non raggiungerà mai il nuovo stato poiché gli ether non potranno mai essere inviati al contratto dell'utente che non accetta ether.

Tecniche di Prevenzione

Nel primo esempio, i contratti non dovrebbero iterare su strutture dati che possono essere manipolate artificialmente da utenti esterni. Si raccomanda un pattern di prelievo, in base al quale ciascun investitore chiama una funzione di prelievo per richiedere i token in modo indipendente.

Nel secondo esempio, era richiesto un utente privilegiato per cambiare lo stato del contratto. In tali esempi (dove possibile) si può utilizzare un fail-safe nel caso in cui l'owner diventi incapace. Una soluzione potrebbe essere quella di configurare l'owner come un contratto multisig. Un'altra soluzione è usare un timelock, in cui la require alla riga [13] potrebbe includere un meccanismo basato sul tempo, come require(msg.sender == owner || now > unlockTime), che consente a qualsiasi utente di finalizzare dopo un periodo di tempo specificato da unlockTime. Questo tipo di tecnica di mitigazione può essere usato anche nel terzo esempio. Se sono richieste chiamate esterne per avanzare a un nuovo stato, bisogna tenere conto del loro possibile fallimento e potenzialmente aggiungere una progressione di stato basata sul tempo nel caso in cui la chiamata desiderata non arrivi mai.

Nota: Naturalmente esistono alternative centralizzate a questi suggerimenti, in cui si può aggiungere un maintenanceUser che possa intervenire e risolvere problemi legati a vettori di attacco DoS se necessario. Tipicamente questi tipi di contratti comportano problemi di fiducia riguardo al potere di tale entità, ma questa non è una discussione per questa sezione.

Esempi dal Mondo Reale: GovernMental

GovernMental era un vecchio schema Ponzi che accumulò una quantità piuttosto elevata di ether. In effetti, a un certo punto aveva accumulato 1100 ether. Sfortunatamente, era suscettibile alle vulnerabilità DoS menzionate in questa sezione. Questo post su Reddit descrive come il contratto richiedesse la cancellazione di un grande mapping per poter prelevare gli ether. La cancellazione di questo mapping aveva un costo di gas che superava il limite di gas del blocco al momento, e quindi non era possibile prelevare i 1100 ether. L'indirizzo del contratto è 0xF45717552f12Ef7cb65e95476F217Ea008167Ae3 e dalla transazione 0x0d80d67202bd9cb6773df8dd2020e7190a1b0793e8ec4fc105257e8128f0506b si può vedere che i 1100 ether sono stati infine ottenuti con una transazione che ha utilizzato 2,5M di gas (dopo che il limite di gas del blocco ha permesso una tale transazione).

12. Manipolazione del Timestamp del Blocco

I timestamp dei blocchi sono stati storicamente utilizzati per una varietà di applicazioni, come entropia per numeri casuali (vedere la sezione Entropy Illusion per ulteriori dettagli), blocco di fondi per periodi di tempo e varie istruzioni condizionali che modificano lo stato e dipendono dal tempo. I miner hanno la capacità di regolare leggermente i timestamp, il che può rivelarsi piuttosto pericoloso se i timestamp dei blocchi vengono usati in modo errato negli smart contract.

Alcuni riferimenti utili a questo proposito sono: The Solidity Docs, questa Domanda su Stack Exchange.

La Vulnerabilità

block.timestamp o il suo alias now può essere manipolato dai miner se hanno un incentivo a farlo. Costruiamo un semplice gioco che sarebbe vulnerabile allo sfruttamento da parte dei miner,

roulette.sol:```solidity contract Roulette { uint public pastBlockTime; // Forces one bet per block

root@kitploit:~
constructor() public payable {} // initially fund contract

// fallback function used to make a bet
function () public payable {
    require(msg.value == 10 ether); // must send 10 ether to play
    require(now != pastBlockTime); // only 1 transaction per block
    pastBlockTime = now;
    if(now % 15 == 0) { // winner
        msg.sender.transfer(this.balance);
    }
}

}

root@kitploit:~
This contract behaves like a simple lottery. One transaction per block can bet `10 ether` for a chance to win the balance of the contract. The assumption here is that, `block.timestamp` is uniformly distributed about the last two digits. If that were the case, there would be a 1/15 chance of winning this lottery.

However, as we know, miners can adjust the timestamp, should they need to.  In this particular case, if enough ether pooled in the contract, a miner who solves a block is incentivised to choose a timestamp such that `block.timestamp` or `now` modulo 15 is `0`. In doing so they may win the ether locked in this contract along with the block reward. As there is only one person allowed to bet per block, this is also vulnerable to [front-running](#race-conditions) attacks.

In practice, block timestamps are monotonically increasing and so miners cannot choose arbitrary block timestamps (they must be larger than their predecessors). They are also limited to setting blocktimes not too far in the future as these blocks will likely be rejected by the network (nodes will not validate blocks whose timestamps are in the future).

<h3 id="block-timestamp-prev">Tecniche Preventive</h3>

Block timestamps should not be used for entropy or generating random numbers - i.e. they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state (if assumed to be random).

Time-sensitive logic is sometimes required; i.e. unlocking contracts (timelocking), completing an ICO after a few weeks or enforcing expiry dates. It is sometimes recommend to use `block.number` (see the [documentazione di Solidity](http://solidity.readthedocs.io/en/latest/units-and-global-variables.html#block-and-transaction-properties)) and an average block time to estimate times; cioè, `1 week` con un tempo di blocco di `10 second` equivale approssimativamente a `60480 blocks`. Thus, specifying a block number at which to change a contract state can be more secure as miners are unable to manipulate the block number as easily. The [BAT ICO](https://etherscan.io/address/0x0d8775f648430679a709e98d2b0cb6250d2887ef#code) contract employed this strategy.

This can be unnecessary if contracts aren't particularly concerned with miner manipulations of the block timestamp, but it is something to be aware of when developing contracts.

<h3 id="block-timestamp-example">Esempio Reale: GovernMental </h3>

[GovernMental](http://governmental.github.io/GovernMental/) was an old Ponzi scheme that accumulated quite a large amount of ether. It was also vulnerable to a timestamp-based attack. The contract payed out to the player who was the last player to join (for at least one minute) in a round. Thus, a miner who was a player, could adjust the timestamp (to a future time, to make it look like a minute had elapsed) to make it appear that the player was the last to join for over a minute (even though this is not true in reality). More detail on this can be found in the [Post sulla Storia delle Vulnerabilità di Sicurezza di Ethereum](https://applicature.com/blog/history-of-ethereum-security-vulnerabilities-hacks-and-their-fixes) by Tanya Bahrynovska.

<h2 id="constructors"><span id="SP-13">13. Costruttori con Cura</span></h2>

Constructors are special functions which often perform critical, privileged tasks when initialising contracts. Before solidity `v0.4.22` constructors were defined as functions that had the same name as the contract that contained them. Thus, when a contract name gets changed in development, if the constructor name isn't changed, it becomes a normal, callable function. As you can imagine, this can (and has) lead to some interesting contract hacks.

For further reading, I suggest the reader attempt the [Sfide di Ethernaught](https://github.com/OpenZeppelin/ethernaut) (in particular the Fallout level).

<h3 id="constructors-vuln">La Vulnerabilità</h3>

If the contract name gets modified, or there is a typo in the constructor's name such that it no longer matches the name of the contract, the constructor will behave like a normal function. This can lead to dire consequences, especially if the constructor is performing privileged operations. Consider the following contract```solidity
contract OwnerWallet {
    address public owner;

    //constructor
    function ownerWallet(address _owner) public {
        owner = _owner;
    }

    // fallback. Collect ether.
    function () payable {}

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

Questo contratto raccoglie ether e consente solo al proprietario di prelevare tutto l'ether chiamando la funzione withdraw(). Il problema nasce dal fatto che il costruttore non è esattamente nominato come il contratto. In particolare, ownerWallet non è lo stesso di OwnerWallet. Pertanto, qualsiasi utente può chiamare la funzione ownerWallet(), impostarsi come proprietario e quindi prendere tutto l'ether nel contratto chiamando withdraw().

Tecniche di prevenzione

Questo problema è stato affrontato principalmente nel compilatore Solidity nella versione 0.4.22. Questa versione ha introdotto una parola chiave constructor che specifica il costruttore, invece di richiedere che il nome della funzione corrisponda al nome del contratto. Si raccomanda di usare questa parola chiave per specificare i costruttori al fine di prevenire i problemi di denominazione evidenziati sopra.

Esempio reale: Rubixi

Rubixi (codice del contratto) era un altro schema piramidale che presentava questo tipo di vulnerabilità. In origine si chiamava DynamicPyramid, ma il nome del contratto è stato cambiato prima della distribuzione in Rubixi. Il nome del costruttore non è stato cambiato, consentendo a qualsiasi utente di diventare il creator. Alcune discussioni interessanti relative a questo bug possono essere trovate in questo Bitcoin Thread. In definitiva, ciò ha permesso agli utenti di contendersi lo stato di creator per richiedere le commissioni dello schema piramidale. Ulteriori dettagli su questo particolare bug sono disponibili qui.

14. Puntatori di storage non inizializzati

L'EVM memorizza i dati come storage o come memory. Comprendere esattamente come ciò avviene e i tipi predefiniti delle variabili locali delle funzioni è altamente raccomandato quando si sviluppano contratti. Questo perché è possibile produrre contratti vulnerabili inizializzando in modo inappropriato le variabili.

Per saperne di più su storage e memory nell'EVM, vedi i Solidity Docs: Data Location, Solidity Docs: Layout of State Variables in Storage, Solidity Docs: Layout in Memory.

Questa sezione si basa sull'eccellente post di Stefan Beyer. Ulteriori letture su questo argomento possono essere trovate nell'ispirazione di Sefan, ovvero questo thread reddit.

La vulnerabilità

Le variabili locali all'interno delle funzioni sono per impostazione predefinita storage o memory a seconda del loro tipo. Le variabili locali storage non inizializzate possono puntare ad altre variabili di storage impreviste nel contratto, portando a vulnerabilità intenzionali (cioè il developer le inserisce deliberatamente per attaccare in seguito) o non intenzionali.

Consideriamo il seguente contratto di registrazione dei nomi relativamente semplice:```solidity // A Locked Name Registrar contract NameRegistrar {

root@kitploit:~
bool public unlocked = false;  // registrar locked, no name updates

struct NameRecord { // map hashes to addresses
    bytes32 name;
    address mappedAddress;
}

mapping(address => NameRecord) public registeredNameRecord; // records who registered names
mapping(bytes32 => address) public resolve; // resolves hashes to addresses

function register(bytes32 _name, address _mappedAddress) public {
    // set up the new NameRecord
    NameRecord newRecord;
    newRecord.name = _name;
    newRecord.mappedAddress = _mappedAddress;

    resolve[_name] = _mappedAddress;
    registeredNameRecord[msg.sender] = newRecord;

    require(unlocked); // only allow registrations if contract is unlocked
}

}

root@kitploit:~
Questo semplice registrar di nomi ha una sola funzione. Quando il contratto è `unlocked`, consente a chiunque di registrare un nome (come hash `bytes32`) e mappare quel nome a un indirizzo. Purtroppo, questo registrar è inizialmente bloccato e il `require` alla riga \[23\] impedisce a `register()` di aggiungere record di nomi. C'è tuttavia una vulnerabilità in questo contratto che consente la registrazione dei nomi indipendentemente dalla variabile `unlocked`.

Per discutere questa vulnerabilità, dobbiamo prima capire come funziona lo storage in Solidity. Come panoramica di alto livello (senza entrare in dettagli tecnici: suggerisco di leggere la documentazione di Solidity per una revisione approfondita), le variabili di stato vengono memorizzate in sequenza in *slot* così come appaiono nel contratto (possono essere raggruppate, ma non in questo esempio, quindi non ci preoccuperemo di questo). Quindi, `unlocked` si trova nello `slot 0`, `registeredNameRecord` nello `slot 1` e `resolve` nello `slot 2`, ecc. Ciascuno di questi slot ha dimensione di 32 byte (ci sono complessità aggiuntive con le mappature, che ignoriamo per ora). Il booleano `unlocked` apparirà come `0x000...0` (64 zeri, escluso `0x`) per `false` o `0x000...1` (63 zeri) per `true`. Come puoi vedere, in questo particolare esempio c'è un notevole spreco di storage.

La successiva informazione di cui abbiamo bisogno è che Solidity imposta come predefinito i tipi di dati complessi, come gli `struct`, a `storage` quando li inizializza come variabili locali. Pertanto, `newRecord` alla riga \[16\] ha come default `storage`. La vulnerabilità è causata dal fatto che `newRecord` non è inizializzata. Poiché il default è storage, diventa un puntatore allo storage e, poiché non è inizializzata, punta allo slot `0` (cioè dove è memorizzato `unlocked`). Nota che nelle righe \[17\] e \[18\] impostiamo `nameRecord.name` a `_name` e `nameRecord.mappedAddress` a `_mappedAddress`; questo di fatto modifica la posizione di storage dello slot 0 e dello slot 1, modificando sia `unlocked` che lo slot di storage associato a `registeredNameRecord`.

Ciò significa che `unlocked` può essere modificato direttamente, semplicemente tramite il parametro `bytes32 _name` della funzione `register()`. Quindi, se l'ultimo byte di `_name` è diverso da zero, modificherà l'ultimo byte dello storage `slot 0` e cambierà direttamente `unlocked` in `true`. Tali valori di `_name` supereranno il `require()` alla riga \[23\] poiché stiamo impostando `unlocked` a `true`. Prova questo in Remix. Nota che la funzione avrà successo se utilizzi un `_name` della forma: `0x0000000000000000000000000000000000000000000000000000000000000001`

<h3 id="storage-prev">Tecniche di prevenzione</h3>

Il compilatore Solidity segnala le variabili di storage non inizializzate come warning, quindi gli sviluppatori dovrebbero prestare molta attenzione a questi avvisi quando creano smart contract. La versione attuale di mist (0.10) non consente di compilare questi contratti. È buona pratica usare esplicitamente le parole chiave `memory` o `storage` quando si ha a che fare con tipi complessi, per garantire che si comportino come previsto. A partire dalla versione `0.5.0` di Solidity, l'uso di `memory` e `storage` è obbligatorio.

<h3 id="storage-example">Esempi reali: Honeypot: OpenAddressLottery e CryptoRoulette</h3>

Un honeypot chiamato OpenAddressLottery ([codice del contratto](https://etherscan.io/address/0x741f1923974464efd0aa70e77800ba5d9ed18902#code)) è stato distribuito e utilizzava questa peculiarità della variabile di storage non inizializzata per raccogliere ether da alcuni aspiranti hacker. Il contratto è piuttosto complesso, quindi lascio la discussione a questo [thread di Reddit](https://www.reddit.com/r/ethdev/comments/7wp363/how_does_this_honeypot_work_it_seems_like_a/) dove l'attacco è spiegato in modo abbastanza chiaro.

Un altro honeypot, CryptoRoulette ([codice del contratto](https://etherscan.io/address/0x8685631276cfcf17a973d92f6dc11645e5158c0c#code)), utilizza anche questo trucco per cercare di raccogliere un po' di ether. Se non riesci a capire come funziona l'attacco, vedi [An analysis of a couple Ethereum honeypot contracts](https://medium.com/@jsanjuas/an-analysis-of-a-couple-ethereum-honeypot-contracts-5c07c95b0a8d) per una panoramica di questo contratto e di altri.

<h2 id="precision"><span id="SP-15">15. Numeri a virgola mobile e precisione</span></h2>

Al momento in cui scrivo (Solidity v0.4.24), i numeri a virgola fissa o mobile non sono supportati. Ciò significa che le rappresentazioni a virgola mobile devono essere realizzate con i tipi interi in Solidity. Questo può portare a errori/vulnerabilità se non implementato correttamente.

Per ulteriori letture, vedi [Ethereum Contract Security Techniques and Tips - Rounding with Integer Division](https://github.com/ethereum/wiki/wiki/Safety#beware-rounding-with-integer-division),

<h3 id="precision-vuln">La vulnerabilità</h3>

Poiché non esiste un tipo a virgola fissa in Solidity, gli sviluppatori devono implementarne uno proprio utilizzando i tipi di dati interi standard. Ci sono diverse insidie in cui gli sviluppatori possono imbattersi durante questo processo. Cercherò di evidenziarne alcune in questa sezione.

Iniziamo con un esempio di codice (ignoriamo per semplicità qualsiasi problema di overflow/underflow).```solidity
contract FunWithNumbers {
    uint constant public tokensPerEth = 10;
    uint constant public weiPerEth = 1e18;
    mapping(address => uint) public balances;

    function buyTokens() public payable {
        uint tokens = msg.value/weiPerEth*tokensPerEth; // convert wei to eth, then multiply by token rate
        balances[msg.sender] += tokens;
    }

    function sellTokens(uint tokens) public {
        require(balances[msg.sender] >= tokens);
        uint eth = tokens/tokensPerEth;
        balances[msg.sender] -= tokens;
        msg.sender.transfer(eth*weiPerEth); //
    }
}

Questo semplice contratto di acquisto/vendita di token presenta alcuni problemi evidenti nell'acquisto e nella vendita di token. Sebbene i calcoli matematici per l'acquisto e la vendita dei token siano corretti, la mancanza di numeri a virgola mobile produrrà risultati errati. Ad esempio, quando si acquistano token alla riga [7], se il valore è inferiore a 1 ether la divisione iniziale darà 0, portando la moltiplicazione finale a 0 (cioè 200 wei diviso per 1e18 weiPerEth equivale a 0). Allo stesso modo, quando si vendono token, qualsiasi token inferiore a 10 produrrà anch'esso 0 ether. In effetti, qui l'arrotondamento è sempre per difetto, quindi vendendo 29 token, si otterranno 2 ether.

Il problema di questo contratto è che la precisione è solo fino all'ether più vicino (cioè 1e18 wei). Questo può diventare complicato quando si ha a che fare con decimals nei token ERC20 quando sono necessarie precisioni maggiori.

Tecniche di prevenzione

Mantenere la giusta precisione nei tuoi smart contract è molto importante, specialmente quando si hanno a che fare rapporti e tassi che riflettono decisioni economiche.

Dovresti assicurarti che qualsiasi rapporto o tasso che utilizzi consenta numeratori grandi nelle frazioni. Ad esempio, nel nostro esempio abbiamo usato il tasso tokensPerEth. Sarebbe stato meglio usare weiPerTokens, che sarebbe un numero grande. Per risolvere la quantità di token si potrebbe fare msg.value/weiPerTokens. Questo darebbe un risultato più preciso.

Un'altra tattica da tenere a mente è prestare attenzione all'ordine delle operazioni. Nell'esempio precedente, il calcolo per acquistare token era msg.value/weiPerEth*tokenPerEth. Nota che la divisione avviene prima della moltiplicazione. Questo esempio avrebbe raggiunto una precisione maggiore se il calcolo avesse eseguito prima la moltiplicazione e poi la divisione, cioè msg.value*tokenPerEth/weiPerEth.

Infine, quando si definisce una precisione arbitraria per i numeri, può essere una buona idea convertire le variabili in una precisione più alta, eseguire tutte le operazioni matematiche e, infine, quando necessario, riconvertirle alla precisione di output. Tipicamente si usano gli uint256 (poiché sono ottimali per il consumo di gas) che offrono circa 60 ordini di grandezza nel loro intervallo, alcuni dei quali possono essere dedicati alla precisione delle operazioni matematiche. Potrebbe essere meglio mantenere tutte le variabili in alta precisione in Solidity e riconvertirle a precisioni più basse nelle app esterne (è essenzialmente così che funziona la variabile decimals nei contratti ERC20 Token). Per vedere esempi di come fare ciò e le librerie per farlo, consiglio di guardare Maker DAO DSMath. Usano nomi un po' strani, WAD e RAY, ma il concetto è utile.

Esempio reale: Ethstick

Non sono riuscito a trovare un buon esempio in cui l'arrotondamento abbia causato un problema grave in un contratto, ma sono sicuro che ce ne siano molti in giro. Sentiti libero di aggiornare questa sezione se ne hai uno buono in mente.

In mancanza di un buon esempio, voglio attirare la tua attenzione su Ethstick principalmente perché mi piace il nome accattivante all'interno del contratto. Questo contratto non usa alcuna precisione estesa, tuttavia lavora con i wei. Quindi questo contratto avrà problemi di arrotondamento, ma solo a livello di precisione dei wei. Ha anche alcuni difetti più seri, ma questi sono legati alla difficoltà di ottenere entropia sulla blockchain (vedi Entropy Illusion). Per un'ulteriore discussione sul contratto Ethstick, ti rimando a un altro post di Peter Venesses, Ethereum Contracts Are Going to be Candy For Hackers.

16. Autenticazione Tx.Origin

Solidity ha una variabile globale, tx.origin, che attraversa l'intero stack di chiamate e restituisce l'indirizzo dell'account che ha originariamente inviato la chiamata (o transazione). Usare questa variabile per l'autenticazione negli smart contract rende il contratto vulnerabile a un attacco simile al phishing.

Per ulteriori letture, vedi Domanda su Stack Exchange, Blog di Peter Venesses e Solidity - attacchi Tx.Origin.

La vulnerabilità

I contratti che autorizzano gli utenti utilizzando la variabile tx.origin sono tipicamente vulnerabili agli attacchi di phishing che possono indurre gli utenti a eseguire azioni autenticate sul contratto vulnerabile.

Considera il semplice contratto,```solidity contract Phishable { address public owner;

root@kitploit:~
constructor (address _owner) {
    owner = _owner;
}

function () public payable {} // collect ether

function withdrawAll(address _recipient) public {
    require(tx.origin == owner);
    _recipient.transfer(this.balance);
}

}

root@kitploit:~
Si noti che alla riga \[11\] questo contratto autorizza la funzione `withdrawAll()` utilizzando `tx.origin`. Questo contratto consente a un attaccante di creare un contratto d'attacco della forma,```solidity
import "Phishable.sol";

contract AttackContract {

    Phishable phishableContract;
    address attacker; // The attackers address to receive funds.

    constructor (Phishable _phishableContract, address _attackerAddress) {
        phishableContract = _phishableContract;
        attacker = _attackerAddress;
    }

    function () payable {
        phishableContract.withdrawAll(attacker);
    }
}

Per utilizzare questo contratto, un attaccante lo distribuirebbe, e poi convincerebbe il proprietario del contratto Phishable a inviare a questo contratto una certa quantità di ether. L'attaccante può mascherare questo contratto come proprio indirizzo privato e manipolare socialmente la vittima affinché invii una qualche forma di transazione a quell'indirizzo. La vittima, a meno che non sia attenta, potrebbe non notare che c'è del codice all'indirizzo dell'attaccante, oppure l'attaccante potrebbe spacciarlo per un portafoglio multisignature o un portafoglio di archiviazione avanzato (ricordate che il codice sorgente dei contratti pubblici non è disponibile per impostazione predefinita).

In ogni caso, se la vittima invia una transazione (con abbastanza gas) all'indirizzo AttackContract, questo invocherà la funzione fallback, che a sua volta chiama la funzione withdrawAll() del contratto Phishable, con il parametro attacker. Ciò comporterà il prelievo di tutti i fondi dal contratto Phishable all'indirizzo attacker. Questo perché l'indirizzo che ha inizializzato per primo la chiamata era la vittima (cioè il owner del contratto Phishable). Pertanto, tx.origin sarà uguale a owner e il require alla riga [11] del contratto Phishable passerà.

Tecniche preventive

tx.origin non dovrebbe essere utilizzato per l'autorizzazione negli smart contract. Questo non vuol dire che la variabile tx.origin non dovrebbe mai essere usata. Ha comunque alcuni casi d'uso legittimi negli smart contract. Ad esempio, se si volesse impedire a contratti esterni di chiamare il contratto corrente, si potrebbe implementare un require della forma require(tx.origin == msg.sender). Questo impedisce che contratti intermedi vengano usati per chiamare il contratto corrente, limitando il contratto a normali indirizzi senza codice.

Esempio Reale: Non Noto

Non conosco exploit pubblicizzati di questa forma nel mondo reale.

Stranezze di Ethereum

Intendo popolare questa sezione con varie stranezze interessanti che vengono scoperte dalla comunità. Queste sono conservate in questo blog perché possono aiutare nello sviluppo di smart contract se si dovessero utilizzare queste stranezze nella pratica.

Ether senza chiave

Gli indirizzi dei contratti sono deterministici, il che significa che possono essere calcolati prima di creare effettivamente l'indirizzo. Questo vale per gli indirizzi che creano contratti e anche per i contratti che generano altri contratti. In effetti, l'indirizzo di un contratto creato è determinato da:

keccak256(rlp.encode([<account_address>, <transaction_nonce>])

In sostanza, l'indirizzo di un contratto è semplicemente l'hash keccak256 dell'account che lo ha creato concatenato con il nonce delle transazioni dell'account[^2]. Lo stesso vale per i contratti, tranne che i nonce dei contratti partono da 1 mentre i nonce delle transazioni degli indirizzi partono da 0.

Ciò significa che, dato un indirizzo Ethereum, possiamo calcolare tutti i possibili indirizzi di contratto che questo indirizzo può generare. Ad esempio, se l'indirizzo 0x123000...000 creasse un contratto alla sua centesima transazione, creerebbe l'indirizzo del contratto keccak256(rlp.encode[0x123...000, 100]), che produrrebbe l'indirizzo del contratto 0xed4cafc88a13f5d58a163e61591b9385b6fe6d1a.

Cosa significa tutto ciò? Significa che puoi inviare ether a un indirizzo pre-determinato (un indirizzo di cui non possiedi la chiave privata, ma su cui sai che uno dei tuoi account può creare un contratto). Puoi inviare ether a quell'indirizzo e poi recuperare l'ether creando successivamente un contratto che viene generato sullo stesso indirizzo. Il costruttore potrebbe essere usato per restituire tutto l'ether precedentemente inviato. Quindi, se qualcuno ottenesse tutte le tue chiavi private Ethereum, sarebbe difficile per l'attaccante scoprire che i tuoi indirizzi Ethereum hanno anche accesso a questo ether nascosto. Infatti, se l'attaccante eseguisse troppe transazioni così che il nonce necessario per accedere al tuo ether venga consumato, sarebbe impossibile recuperare il tuo ether nascosto.

Lasciate che chiarisca questo con un contratto.```solidity contract KeylessHiddenEthCreator { uint public currentContractNonce = 1; // keep track of this contracts nonce publicly (it's also found in the contracts state)

root@kitploit:~
// determine future addresses which can hide ether.
function futureAddresses(uint8 nonce) public view returns (address) {
    if(nonce == 0) {
        return address(keccak256(0xd6, 0x94, this, 0x80));
    }
    return address(keccak256(0xd6, 0x94, this, nonce));
// need to implement rlp encoding properly for a full range of nonces
}

// increment the contract nonce or retrieve ether from a hidden/key-less account
// provided the nonce is correct
function retrieveHiddenEther(address beneficiary) public returns (address) {
    currentContractNonce +=1;
    return new RecoverContract(beneficiary);
}

Read more

Scarica lo strumento
  • Attack.sol - Riga [25] - L'ether inviato al contratto malevolo eseguirà quindi la funzione fallback.

  • Attack.sol - Riga [26] - Il saldo totale del contratto EtherStore era 10 ether e ora è 9 ether, quindi questa istruzione if viene superata.

  • Attack.sol - Riga [27] - La funzione fallback chiama quindi di nuovo la funzione withdrawFunds() di EtherStore e rientra nel contratto EtherStore.

  • EtherStore.sol - Riga [11] - In questa seconda chiamata a withdrawFunds(), il nostro saldo è ancora 1 ether poiché la riga [18] non è stata ancora eseguita. Pertanto, abbiamo ancora balances[0x0..123] = 1 ether. Lo stesso vale per la variabile lastWithdrawTime. Ancora una volta, superiamo tutti i requisiti.

  • EtherStore.sol - Riga [17] - Preleviamo un altro 1 ether.

  • I passaggi 4-8 si ripeteranno - finché EtherStore.balance >= 1 come stabilito dalla riga [26] in Attack.sol.

  • Attack.sol - Riga [26] - Una volta che nel contratto EtherStore rimane 1 ether (o meno), questa istruzione if fallirà. Questo permetterà quindi l'esecuzione delle righe [18] e [19] del contratto EtherStore (per ogni chiamata alla funzione withdrawFunds()).

  • EtherStore.sol - Righe [18] e [19] - Le mappature balances e lastWithdrawTime verranno impostate e l'esecuzione terminerà.

  • start
    FibonacciBalance
    start
    slot[1]
    fibonacci(n)
    fibonacciLibrary
    uint
    withdraw()
    uint(fibonacciLibrary)
    calculatedFibNumber
    fibonacciLibrary