Sicurezza e audit degli smart contract

Spero di fare cosa gradita pubblicando la lecture che ho tenuto nel 2019 alla Scientific School on Blockchain and DLT sulla sicurezza e ispezione del codice degli smart contract. Il 2020 è un anno pieno di incognite, quale migliore occasione di usare la rete come veicolo di diffusione. Nell’attesa di confermare l’appuntamento per la School 2020 vi auguro buona lettura.

Hack all’exchange Binance, breve ricostruzione dei fatti

Segue una breve cronistoria di cosa è stato annunciato e discusso dal CEO di Binance (Changpeng Zhao, di seguito CZ) durante le ore successive all’hack in cui sono stati sottratti, ricordiamolo, 7000 BTC equivalenti a circa $40M.

Ricordiamo che Binance è uno degli exchange di maggior successo e che soprattutto nel 2018, in un periodo di forte crisi per tutto il settore, si è differenziato dalla concorrenza e il suo token BNB ha avuto delle prestazioni eccezionali.

Tutto inizia con un breve tweet su una ipotetica manutenzione non prevista. D’ora in poi questo sarà il modo per annunciare un hack suppongo.

Una nota un po’ più esplicativa dal sito di Binance spiega che c’è stato un data breach, che molti account sono stati compromessi e che gli stessi hacker sono stati in grado di trasferire 7000 BTC attraverso un’unica transazione.

https://www.blockchain.com/btc/tx/e8b406091959700dbffcff30a60b190133721e5c39e89bb5fe23c5a554ab05ea

We have discovered a large scale security breach today, May 7, 2019 at 17:15:24 (UTC). Hackers were able to obtain a large number of user API keys, 2FA codes, and potentially other info. The hackers used a variety of techniques, including phishing, viruses and other attacks. We are still concluding all possible methods used. There may also be additional affected accounts that have not been identified yet.

Naturalmente inizia un carosello di reazioni e commenti. Una fra tutte quella del founder di Tron che offre a CZ 7000 BTC dal suo conto personale, senza batter ciglio.

La proposta suona un po’ inutile e inopportuna, visto che gli utenti sono coperti dalla formula assicurativa, la famosa SAFU. Tanto che gli utenti si sbizzarriscono in vari commenti dei quali riporto i più moderati.

Elegantemente CZ declina l’offerta di Justin Sun. Mica è un poveraccio che accetta 7000 BTC dal primo venuto.

Gli exchange si mobilitano per isolare gli address degli hacker in modo da non permettere una facile exit / conversione dei fondi rubati in altra valuta.

Come descritto nella nota relativa all’incidente, gli hacker sono stati pazienti e hanno lavorato nell’ombra per compromettere una moltitudine di account dotati di fondi consistenti per poi sferrare l’attacco

Le contromisure annunciate da Binance:

  • stop depositi e prelievi
  • cambio delle API keys
  • cambio dei second factor 2f masterseeds
  • indagine interna per sradicare altri account compromessi

Il famigerato re-org della blockchain

Quello che causa maggior interesse e scatena commenti e discussioni al limite dell’insulto è l’accenno di CZ ad un possibile rollback, o meglio ad una riorganizzazione della blockchain per sottrarre i fondi agli hacker e trasferirli nelle mani dei miner. Una soluzione che non avrebbe riportato indietro i bitcoin ai wallet di Binance ma che almeno avrebbe impedito agli hacker di utilizzarli. L’argomento viene trattato brevemente durante un live AMA dove CZ risponde alle domande degli utenti e dei “curiosi”
https://twitter.com/binance/status/1125959459782553600

Tuttavia, Il reorg approach sembra presto uscire dalla sfera delle opzioni possibili

Alcuni commentatori fanno notare che in effetti non è che sia impossibile, visto che il protocollo non lo impedisce, ma di fatto è la community che lo respinge per tutelare la credibilità di Bitcoin come rete.

CZ sostiene di aver valutato rapidamente questa ipotesi che tralaltro non è stata formulata da lui o da qualcuno di Binance, ma suggerita via tweet da Jeremy Rubin, uno sviluppatore del progetto Bitcoin Core.

Jeremy considera questa una strategia da adoperare per ogni hack, preferibilmente entro i primi sei blocchi, per mantenere quella religiosa convenzione che dopo sei blocchi una transazione Bitcoin dovrebbe essere immutabile.

Come si è comportanto Binance?

Chiaramente sarebbe meglio prevenire gli hack piuttosto che gestire la situazione a posteriori. Comunque nessuno è immune dagli attacchi e maggiori sono le somme coinvolte maggiori saranno le probabilità che qualcuno stia orchestrando un attacco. Secondo alcuni analisti la risposta di Binance e del suo CEO è stata opportuna e tempestiva.

https://decryptmedia.com/6930/binance-hack-security-breach

“Responding to the security breach, CEO at blockchain analytics firm CipherTrace, Dave Jevans, said, “Binance responded quickly to the hack and was very transparent about the ordeal. It is a shining example in the industry of rapid response, full transparency and a solid financial model for reimbursing customers from hacks.”

Jevans pointed out it was the second exchange hack using two-factor authentication this week, recommending a more stringent three-factor authentication. However this will be down to exchanges to implement.

Binance said it will undertake a security review to determine what went wrong and what can be fixed”

Commenti sul BatchTransfer bug in ERC20 e i fantastiliardi di $$$

L’ultima mazzata alla credibilità di Ethereum viene da queste news di Aprile che descrivono come da una semplice transazione del BeautyToken BEC di non so quanti trilioni di trilioni di token ognuno con un controvalore di circa $0.32 sarebbe la più grande transazione commerciale della storia. Vedere per credere i massimi token holder al momento

https://etherscan.io/token/0xc5d105e63711398af9bbff092d4b6769c82f793d#balances

Purtroppo, pare che questo bug non sia limitato al solo BEC token ma una dozzina di altri token abbiano evidentemente copy/pasted lo stesso codice. Qui una lista di quelli noti

 

Ovviamente di tratta di glitch, bugs, exploit di qualche malfunzionamento del codice. Come risposta gli exchange hanno chiuso tutte le transazioni di token ERC20 (per chi non lo sapesse ERC20 sono le specifiche che descrivono come uno smart contract Ethereum deve essere fatto per funzionare come un token).

Sembra che tutto il problema derivi da questa funzione batchTransfer ( )

batchoverflow.jpg

che vorrebbe in pratica con una sola transazione spedire un po’ di token a diversi destinatari. Una specie di airdrop insomma.

Ma si sa, la programmazione è la diabolica arte di inserire bug nei programmi, oppure se preferite un programma è il codice che sembra funzionare tra un bug e l’altro. Insomma per farla breve questo codice presenta un problema di overflow.

2 + 2 = 0

se il vostro registro è fatto con soli due bit. Se i bit sono 256 il concetto non cambia.

A prima vista si direbbe che i devs fossero consapevoli dei problemi di overflow perché hanno utilizzato la SafeMath library (questo lo capiamo dal fatto che somme e sottrazioni sono nella forma .sub( ) e .add( )

Importare e usare queste librerie che sono scritte apposta per evitare gli overflow è una condizione necessaria ma non sufficiente. Soprattutto se da qualche parte ti dimentichi di usarla come avviene nella riga 257. Passando alla funzione batchTransfer( ) dei parametri opportunamente forgiati:

batchTransfer([addressA,addressB],0x8000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000)

avviene il fattaccio, ovvero che la variabile amount nella linea 257 diventa

uint256 amount = 2 * 0x8000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000

che è esattamente 0 a causa dell’overflow.

Quindi il successivo require alla linea 259 risulta rispettato e il codice successivo viene eseguito quando invece non dovrebbe.

Risultato finale: addressB e addressA ricevono dal nulla la modesta cifra di 0x8000<altri 60 zeri a seguire> token che significa 2^255 token a testa. Non male.

Di chi è la colpa?

Colpa di Ethereum!!!  colpa di Vitalik !!!  colpa dei token !!!.

Si è letto un po’ di tutto in giro. Soprattutto FUD. Ci sta che gli exchange sospendano le transazioni ERC20 per verificare quali di questi token tra le migliaia ormai listate possano soffrire della stessa vulnerabilità. Ma bisogna specificare che questo è un bug di chi ha scritto lo smart contract per questi token e non è un bug della piattaforma Ethereum.

Secondo punto, non è nemmeno qualcosa che deriva dallo standard ERC20 che non prevede la possibilità di fare questo batchTransfer( ).  Ricordiamo quali sono le funzioni previste per ERC20

Screenshot 2018-04-29 16.18.50.png

Quindi non è colpa di Vitalik se non nella misura in cui Ethereum è stato progettato per permettere a chiunque di mettere user code in blockchain. Ma questa in fondo è la più grande forza di Ethereum, senza di questa ci bastava Bitcoin (e per molti effettivamente è così).

 

 

 

 

Si fa presto a dire smart: caveats per smart contract sicuri – parte 1

Sempre più persone sentono parlare di ICO e quindi si immaginano di ottenere soldi facili con un wallet online aperto come un cesto di basketball dove gli investitori non vedono l’ora di schiacciar dentro palloni foderati di ether, bitcoin, litecoin e tutti gli altri coin della famiglia.

Il tutto perché si tratta di smart contract, sono lì apposta. Ora a parte la difficoltà di metter su una vera ICO, difficoltà che aumenta ogni giorno perché ogni giorno l’asticella si sposta verso l’alto e la competizione è in crescita esponenziale, ci sono anche aspetti meramente tecnici su cui riflettere.

ICO a parte ogni smart contract è sempre un pezzo di “programmable money”. Anzi, nel momento in cui viene messo in moto sulla blockchain è un pezzo di “programmed money” e come ben sanno tutti quelli che hanno scritto un hello world non è possibile scrivere un software senza metterci dentro, involontariamente si spera, un bug.

“In accordance with Unix philosophy, Perl gives you enough rope to hang yourself” –Larry Wall

Un bug che colpisce un programma già è una cosa seccante, se colpisce il nostro denaro diventa un disastro. A differenza di un normale sistema in produzione che una volta individuato il bug si può fixare e riavviare – e pazienza per il downtime – quando succede ad uno smart contract in realtà ripristinare lo stato corretto di esecuzione non è scontato. Si può sostituire un contract con uno corretto, ma non è detto che si possano recuperare gli ether. Infatti non si tratta di recuperare dati, si tratta di recuperare gli effetti di transazioni irrevocabili. Per questa ragione Ethereum sembra un progetto pazzesco. Tanto pazzesco che però piace a molti e sta diventando la piattaforma di riferimento per mettere bug nei soldi, ops … per creare smart contract.

Se nel caso del software normale l’audit del codice sembra quasi una pratica da sbrigare per compiacenza generale di clienti e colleghi, nel caso degli smart contract diventa una pratica alla quale non vorremo mai rinunciare.

Screenshot 2017-10-14 15.43.21

Auditor dove siete?

Sempre più progetti pubblicano i risultati dei loro audit su codice solidity e la cosa è molto apprezzabile, ci consente di imparare molto dagli audit degli altri e di capire meglio lo stato dell’arte su cos’è considerato un codice “relativamente” sicuro, o per lo meno un codice audited.

Uno dei framework più affermati per iniziare, almeno iniziare, con codice sicuro (per quanto si possa essere sicuri di qualcosa) è sicuramente Open Zeppelin. Tra le librerie fornite da Open Zeppelin c’è Safe Math

 

SafeMath

Il problema con solidity è che non fornisce un controllo sull’overflow della somma fra due numeri, o in generale sul risultato di un’operazione aritmetica. Ad esempio nel caso della somma se questa supera il max integer value di (2^256-1). Safe Math aggiunge un controllo sul risultato. Allo stesso modo per molte altre operazioni aritmetiche.

function safeAdd(uint256 x, uint256 y) internal returns(uint256) {

 uint256 z = x + y;
 assert((z >= x) && (z >= y));
 return z;
}

Naturalmente usare SafeMath per ogni operazione consuma più gas, quindi se siamo sicuri di non “sforare” con le operazioni aritmetiche non bisognerebbe usarlo.

 

Reentrancy

Un altro tipico problema è la reentrancy. Quando una funzione spedisce ether ad un account esterno in generale questo potrebbe essere un altro contratto. Un contratto può essere programmato per eseguire codice nel momento in cui riceve ether e in modo malevolo potrebbe re-invocare la funzione che lo sta “beneficiando” con lo scopo di sifonarla. L’errore tipico è spedire una quota di ether ad un indirizzo e solo dopo mettere a zero il suo balance in una tabella, come nell’esempio sotto.

 

// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund {
  /// Mapping of ether shares of the contract.
  mapping(address => uint) shares;
  /// Withdraw your share.
  function withdraw() {
  if (msg.sender.send(shares[msg.sender]))
    shares[msg.sender] = 0;
  }
}

 

msg.sender potrebbe a sua volta invocare ancora withdraw( … ) prima che l’istruzione shares[msg.sender] = 0 venga eseguita. Un esempio di questo exploit è riportato qui

Un errore simile a questo è stato alla base del DAOsaster. Una versione corretta del “prelievo” prima di tutto mette a zero il balance del prelevante e solo dopo gli spedisce i suoi ether.

contract Fund {
 /// Mapping of ether shares of the contract.
 mapping(address => uint) shares;
 /// Withdraw your share.
 function withdraw() {
   var share = shares[msg.sender];
   shares[msg.sender] = 0;
   msg.sender.transfer(share);
 }
}

Ultimo caso analizzato in questa parte del post è un caso in realtà non legato a Ethereum ma valido in generale per tutti i linguaggi con blocchi di codice separati da parentesi (python è immune), non fate if su più linee senza parentesi.

 

Multi line if without brackets

function destroyDeed() {
 if (active) throw;
 if(owner.send(this.balance))
 selfdestruct(burn);
}

Questo stile di programmazione porta molto probabilmente un refactoring o un futuro sviluppo del codice ad aggiungere qualche istruzione in modo errato, come sotto.

function destroyDeed() {
 if (active) throw;
 if(owner.send(this.balance))
 runPreDestroy();
 selfdestruct(burn);
}