Enkele valkuilen met smart contracts in Ethereum

Posted on: 20/02/2018 by: Kristof Verslype

Ethereum is het meest populaire blockchain-platform voor ‘smart contracts’. Dit zijn in feite stukjes code die gedistribueerd worden uitgevoerd binnen een blockchain-netwerk. Voor iemand met een beetje programmeerachtergrond ziet een smart contract in Ethereum eruit als normale code. Toch zijn er specifieke valkuilen waarop je moet letten en waar zelfs kenners zich tegen bezondigen. Twee denkfouten hieronder geven aan dat de realiteit toch wat complexer is dat ze op het eerste zicht doet vermoeden.

Alle parameters mee in de blockchain?

Eind 2016, op een seminarie over smart contracts, probeerde één van de sprekers ons te overtuigen van de eenvoud van deze technologie. Helaas maakte hij zelf een fundamentele fout. Daaruit bleek dat hij eigenlijk – net zoals het publiek – nog onvoldoende de details over zijn onderwerp beheerste.

Hij toonde een smart contract dat een notaris toeliet om aan te geven dat een document verwerkt was. De code wordt – licht vereenvoudigd – weergegeven in figuur 1. De notaris registreert een document via de functie notarize(). Deze functie voegt normaliter aan de variabele ‘proofs’ een element toe dat bestaat uit de unieke sha256-vingerafdruk van het document (32-bytes hash), en de waarde ‘true’. Op het eerste gezicht lijkt dit prima.

Verkeerd doorgeven van parameters

Figuur 1: Verkeerd doorgeven van parameters

Het oproepen van een functie die de contracttoestand wijzigt, gebeurt hier echter via een transactie die op de blockchain terechtkomt. Om de notarize()-functie uit te voeren, creëert de notaris met zijn private sleutel een transactie. Daarin zit onder meer het adres van het smart contract, de identificatiesleutel van de functie, en ook – dit is essentieel hier – de parameters die aan de functie meegegeven worden.

Hoewel enkel de vingerafdruk van het document in het smart contract terechtkomt, wordt dus het volledige document (!) in een transactie op de blockchain bewaard. Dat maakt niet enkel de blockchain al gauw onhandelbaar groot. Bovendien worden alle verwerkte documenten, die uiteraard gevoelige gegevens bevatten, voor de hele wereld toegankelijk. Eens een document in de Ethereum-blockchain terechtkomt, kan het in principe niet meer verwijderd of gewijzigd worden.

Figuur 2. Gecorrigeerd maar niet zo nuttig smart contract

Figuur 2. Gecorrigeerd maar niet zo nuttig smart contract

Maar er is meer. Wanneer een notaris code wil uitvoeren op een smart contract, dan moet hij daarvoor betalen. Ethereum-miners zullen immers alleen bereid zijn om je transactie te verwerken, als ze daarvoor een voldoende hoge vergoeding krijgen. Je betaalt meer voor transacties die meer data bevatten, en je betaalt ook meer wanneer er meer rekenkracht en/of opslag vereist is voor het uitvoeren van de code zelf. Samen zou het oproepen van deze notarize()-functie voor een document van 1MB eind december 2017 al een kleine 2000 dollar gekost hebben.

Al deze zaken kunnen eenvoudig verholpen worden door de vingerafdruk (hash) op de client te berekenen en enkel dit als parameter mee te geven aan de notarize()-functie. Dat resulteert in de code in figuur 2. De ironie is dat we in het smart contract nu gewoon sleutelparen opslaan – wat evengoed kan zonder smart contracts. Een erg overtuigend voorbeeld om de meerwaarde van smart contracts te aan te tonen, is dit dus helaas niet.

Een contract is zelf ook een rekening

In het voorjaar van 2016 werd met blockchain een ‘decentraal risicokapitaalfonds’ opgericht, genaamd ‘The DAO’ [1]. De basis is een set van smart contracts waarop je met Ether, de cryptomunt van Ethereum, stemrecht koopt. Hoe meer geld je in het contract stort, hoe meer stemrecht je hebt. Er konden projecten ingediend worden en wanneer een project voldoende ja-stemmen haalde, kreeg het financiering.

Op een gegeven moment bevatte het fonds 168 miljoen dollar aan Ether; wat gelijk was aan 14% van alle op dat moment bestaande Ether. Helaas bevatte het smart contract een bug, waardoor er 54 miljoen dollar weglekte naar de aanvaller. De meerderheid van de miners ging akkoord om de blockchain terug te draaien, waardoor de meest recente geschiedenis van de blockchain collectief werd geschrapt en de aanval dus, volgens de blockchain althans, nooit gebeurd was.

Waar liep het fout? Om te participeren in open, publieke blockchain netwerken zoals Bitcoin en Ethereum registreer je eerst een externe account. Je maakt lokaal een publiek-privaat sleutelpaar aan. De private sleutel hou je geheim. Een vingerafdruk van je publieke sleutel (20-bytes hash) is je adres. Met dit adres als pseudoniem, ben je gekend op de blockchain. Anderen kunnen dan cryptogeld storten naar dit adres en met je private sleutel kun je dit geld transfereren naar andere adressen, of kun je smart contracts publiceren en/of gebruiken.

Maar, ook een contract heeft een adres en is in staat om cryptogeld te ontvangen, vast te houden en te spenderen! In dit geval spreken we van een contract account. Wanneer een smart contract geld ontvangt, zonder dat expliciet gevraagd wordt om een bepaalde functie in het smart contract uit te voeren, dan wordt de basisfunctie uitgevoerd. Die noteren we als: function (){…}.

Figuur 3. Zolang de splitDAO uitgevoerd wordt door een external account is er geen probleem.

Figuur 3. Zolang de splitDAO uitgevoerd wordt door een external account is er geen probleem.

Wanneer een externe account de functie splitDAO() oproept, zoals in figuur 3, kan onrechtstreeks de payOut()-functie opgeroepen worden, die een bedrag: (_amount) naar het adres van de oproeper stort. Pas nadien zal de functie splitDAO() o.a. de balans van de gebruiker in het smart contract overeenkomstig op nul zetten.

Figuur 4. Wanneer splitDAO uitgevoerd wordt door een contract account, is er een probleem.

Figuur 4. Wanneer splitDAO uitgevoerd wordt door een contract account, is er een probleem.

Het loopt fout [2] wanneer de oproeper van de functie splitDAO() geen externe account is, maar zélf een contract account. Daardoor wordt een recursieve aanval mogelijk, zoals geïllustreerd in figuur 4:

  1. Het contract van de aanvaller roept de splitDAO()-functie op.
  2. Daardoor stort The DAO een bedrag (_amount) naar het contract van de aanvaller.
  3. Dit resulteert in het oproepen van de basisfunctie van het aanvallende contract.
  4. De basisfunctie van het aanvallende contract voert opnieuw de splitDAO()-functie uit. We springen opnieuw naar stap 2.

Deze aanval kan een tijdje doorgaan zonder dat de laatste regels van de splitDAO()-functie uitgevoerd worden – dus zonder dat de balans van het aanvallende contract (balances[msg.sender]) op nul wordt gezet en waardoor steeds hetzelfde bedrag naar het aanvallende contract gestort wordt.

Uiteindelijk stopt de uitvoering: hetzij omdat het geld dat de aanvaller meestuurt om code uit te voeren is opgebruikt, hetzij omdat de stack-limiet is bereikt, hetzij omdat er geen geld meer in The DAO aanwezig is. Behalve in het laatste geval kan de aanval probleemloos herhaald worden.

Conclusies

Dit artikel gaat in op twee programmeervalkuilen in Ethereum smart contracts. Er zijn nog heel wat andere voorbeelden van – achteraf gezien – triviale bugs [3, 4, 5], waarmee soms veel geld verloren ging. In november 2017 ging nog zo’n 300 miljoen dollar verloren. Om het risico op bugs te reduceren laat u uw smart contract voor publicatie dus best nakijken door een deskundige.

Bovendien blijven ook klassieke aanvallen bestaan [6], waarbij bijvoorbeeld de aanvaller de website van een bedrijf hackt, en het adres van een smart contract van het bedrijf vervangt door een adres van zijn eigen smart contract. Ook phising blijft een klassieker: de aanvaller stuurt een mail die afkomstig lijkt van een serieus bedrijf, met de vraag om cryptogeld te investeren in een interessant smart contract. Uiteraard is dit niet het smart contract van het bedrijf, maar wel van de aanvaller.

Referenties

[1] The DAO (organization). Wikipedia.
https://en.wikipedia.org/wiki/The_DAO_(organization)

[2] Analysis of the DAO exploit. Phil Daian. Hacking Distributed.
http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/

[3] '$300m in cryptocurrency' accidentally lost forever due to bug. Alex Hern. The Guardian. 8 Novmber 2017.
https://www.theguardian.com/technology/2017/nov/08/cryptocurrency-300m-dollars-stolen-bug-ether

[4] 'THIS IS NOT A DRILL:' A Hacker Allegedly Stole $32 Million in Ethereum. Jordan Pearson. Motherboard, 19 juli 2017.
https://motherboard.vice.com/en_us/article/zmvkke/this-is-not-a-drill-a-hacker-allegedly-stole-dollar32-million-in-ethereum

[5] A survey of attacks on Ethereum smart contracts. Nicola Atzei, Massimo Bartoletti, Tiziana Cimoli. Proceedings of the 6th International Conference on Principles of Security and Trust - Volume 10204. Pages 164-186. April 2017.