Towards a Toolchain for Exploiting Smart Contracts on the Blockchain

by Sebastian Kindler M.A., University of Bayreuth, 2011

Thesis Submitted in Partial Fulfillment of the Requirements for the Degree of Bachelor of Science in the Computer Science Program Faculty of Computer Science

Supervisor: Prof. Dr. Stefan Traub Second Assessor: Prof. Dr. Markus Schäffter External Assessor: Dr. Henning Kopp

Ulm University of Applied Sciences March 22, 2019 Abstract

The present work introduces the reader to the Ethereum blockchain. First, on a con- ceptual level, explaining general blockchain concepts, and viewing the Ethereum blockchain in particular from different perspectives. Second, on a practical level, the main components that make up the Ethereum blockchain are explained in detail. In preparation for the objective of the present work, which is the analysis of EVM bytecode from an attacker’s perspective, smart contracts are introduced. Both, on the level of EVM bytecode and Solidity source code. In addition, critical assem- bly instructions relevant to the exploitation of smart contracts are explained in detail. Equipped with a definition of what constitutes a vulnerable contract, further practical and theoretical aspects are discussed: The present work introduces re- quirements for a possible smart contract analysis toolchain. The requirements are viewed individually, and theoretical focus is put on automated bytecode analysis and symbolic execution as this is the underlying technique of automated smart contract analysis tools. The importance of semantics is highlighted with respect to designing automated tools for smart contract exploitation. At the end, a min- imal toolchain is presented, which allows beginners to efficiently analyze smart contracts and develop exploits.

i Contents

Introduction1

1 Preliminaries3

1.1 Blockchain...... 3

1.2 Ethereum...... 10

1.2.1 Ethereum from Different Perspectives...... 10

1.2.2 Ethereum World State σ ...... 13

1.2.3 Ethereum Account Types...... 15

1.2.4 Ethereum Transactions...... 16

1.2.5 Ethereum Virtual Machine (EVM)...... 23

1.2.6 Ethereum Peer-to-Peer Network...... 26

1.3 Smart Contracts...... 28

1.3.1 Smart Contracts at EVM Bytecode Level...... 29

1.3.2 Solidity and the Structure of Smart Contracts...... 35

1.4 Vulnerability of Smart Contracts...... 43

1.4.1 Critical Bytecode Instructions in Smart Contracts..... 43

1.4.2 Exploitation of Critical Instructions...... 46

1.4.3 Defining Vulnerable Smart Contracts and Exploits.... 49

ii 2 Towards a Smart Contract Exploit Development Toolchain 50

2.1 Requirements Analysis...... 50

2.2 Requirement 1: EVM Bytecode Deployment...... 53

2.3 Requirement 2: Manual EVM Bytecode Analysis (Tools)..... 55

2.4 Requirement 3: Automated Analysis (Theory)...... 57

2.4.1 Symbolic Execution...... 57

2.5 Requirement 4: Automated Exploit Development...... 70

2.6 Toolchain for Automated EVM Bytecode Analysis and Exploit Development...... 73

Conclusions 74

iii Introduction

The concept of smart contracts was introduced in 1994 by cryptographer Nick Szabo [46], who offers the following definition:

A smart contract is a computerized transaction protocol that executes the terms of a contract. The general objectives of smart contract design are to satisfy common contractual conditions (such as payment terms, liens, confidentiality, and even enforcement), minimize exceptions both malicious and accidental, and minimize the need for trusted intermediaries. Related economic goals include lowering fraud loss, arbitration and enforcement costs, and other transaction costs1.

A smart contract is as binding as a legal contract. The bytecode of a smart contract constitutes the contractual conditions, to which users subject themselves when they execute the respective smart contract. However, unlike a legal contract, a smart contract can neither be circumvented nor fought in court. As programs, and from the perspective of the end user, smart contracts execute precisely the way they are designed. In this sense, Ethereum blockchain technology is an implementation of a decentralized crypto-law system [51]. In contrast to national legal systems, Ethereum non-contract account owners do not decide by which law they want to abide, but rather by which law they want to be bound. Once called via a message call transaction, smart contract execution cannot be stopped, and the contractual conditions are binding in the absolute sense. However, if program code is what constitutes the law, then programming errors are part of the law as well. Hence, by definition, the abuse of programming errors in a

1The term transaction costs goes back to the article The problem of Social Cost by Ronald Coase [10], the theses of which were later summarized as the Coase theorem [12]. Transaction costs have a negative connotation and refer to the time and effort as well as the resources that are required to negotiate the exchange of legal entitlements. According to the interpretation [12] of Coase’s proposal, from the perspective of efficiency, the original allocation of resources is of no concern as long as transactions of legal entitlements are costless. Reducing transaction costs facilitates the efficient exchange of legal entitlements and increases cooperation between competing parties.

1 decentralized system such as Ethereum cannot constitute a violation of the system’s crypto-law. Any condemnation of attacks against error-prone smart contracts builds on the remnants of thinking in centralized legal systems. A decentralized system thus eradicates such thinking. Public blockchain implementations such as Ethereum are transparent but trustless environments. However, the trust people put in Ethereum does not depend on centralized legal institutions. Rather, people put their trust in algorithms [35] the security of blockchain technology is build on. Regarding the consensus algorithm, i.e., the proof-of-work, such trust may be justified. However, complete trust in the correct execution of arbitrary programs seems ill-placed. Especially, when these programs manage ’real’ people’s money: Ethereum smart contracts own Ether worth millions of US dollar, and heists [33] on the Ethereum blockchain have shown how vulnerable and insecure smart contracts can be. To comprehend the severity of smart contract vulnerabilities as well as the importance of a toolchain for smart contract vulnerability analysis, the subsequent work serves as a thorough introduction to the Ethereum blockchain. The present work introduces the reader to the Ethereum blockchain. First, on a conceptual level, explaining general blockchain concepts, and viewing the Ethereum blockchain in particular from different perspectives. Second, on a practical level, the main components that make up the Ethereum blockchain are explained in detail. In preparation for the objective of the present work, which is the analysis of EVM bytecode from an attacker’s perspective, smart contracts are introduced. Both, on the level of EVM bytecode and Solidity source code. In addition, critical assembly instructions relevant to the exploitation of smart contracts are explained in detail. Equipped with a definition of what constitutes a vulnerable contract, further practical and theoretical aspects are discussed: The present work introduces requirements for a possible smart contract analysis toolchain. The requirements are viewed individually, and theoretical focus is put on automated bytecode analysis and symbolic execution as this is the underlying technique of automated smart contract analysis tools. The importance of semantics is highlighted with respect to designing automated tools for smart contract exploitation. At the end, a minimal toolchain is presented, which allows beginners to efficiently analyze smart contracts and develop exploits.

2 1 Preliminaries

1.1 Blockchain

Blockchain as an append-only linked list

The term blockchain refers to a data structure, which can be loosely described as an append-only linked list, whose data content and sequence of data elements are immutable. In comparison, a standard singly linked list is a linear sequence of individual data elements, each of which contains some data and a reference to the next element. Thus, the elements themselves implement the list by referencing the address of the respective next element as shown in Figure1. Each of the elements in the singly linked list can be modified, moved within the sequence or be deleted. Moreover, new elements can be inserted at any point in the sequence: at the beginning, the middle or the end. Thus, a singly linked list is neither append-only nor is it immutable with regards to data content and sequence of data elements.

address : 0x0004 address : 0x000A address : 0x0032

data 12 data 35 data 17

next 0x000A next 0x0032 next Null

F irst element Second element Last element

Figure 1: A singly linked list consisting of three elements, each of which stores an integer value and references the next element by pointing to its address.

In contrast, a blockchain is designed as an append-only linked list: the linear sequence of previously added data elements is immutable, so is the data stored in the data elements. The data elements on a blockchain are referred to as blocks. Each block is comprised of two sections: (1) a block header that contains various pieces of information particular to a block on the blockchain, and (2) a data section,

3 which contains the data, i.e., records of information. Just like the elements added to a linked list, the blocks on a blockchain contain a reference to a neighboring block on the blockchain. The reference to the neighboring block is stored in the block header. However, instead of referencing the next data element, each block references the previous block in the sequence, i.e., the parent block. Instead of using a pointer to the address of the next element in the sequence, each block contains the hash value of its parent block as depicted in Figure2.

block header : block header : block header : [...] [...] [...] fHash(Block #0) fHash(Block #1)

data section : data section : data section : dataA dataB dataC

Block #0 Block #1 Block #2

Figure 2: In a blockchain, the elements of the linear sequence are linked through the hash value of the respective precursor. The hash function fHash() takes the entire block, i.e., block header and data section, as input and produces a hash value as output. The placeholder [...] in the block header represents additional pieces of information that are particular to a block.

Blockchain cryptographically protected by hash values

The properties of hash values allow for data validation and provide a means for checking the integrity of the data stored on the blockchain: Hash functions take an arbitrarily large size of data as input and map it to a fixed-sized output, e.g., a 256-bit hash value. In addition, good hash functions map data in a way that exhibits the so-called avalanche effect: If only one bit of the input data is inverted, each of the bits of the output hash value will change with a probability of fifty percent [50, p. 524]. This makes the output of hash functions entirely unpredictable. Only identical blocks of data will produce the same hash value.

4 Thus, hash functions create a fingerprint of the respective data, which can be used to check the data’s integrity. In this way, each block references its precursor and cryptographically contains the identity of the precursor’s data in the form of a fingerprint, i.e., the hash value. When a block is added to the end of the sequence, the hash value of the last block in the blockchain becomes part of the new block’s header information, and subsequently determines the new block’s hash value. Modifying, appending or deleting even one character in a block’s data, therefore, would change that block’s hash value significantly as shown in Figure 3. Hence, linking blocks on the blockchain through their hash values is a way of providing a means for detecting manipulation of the data stored on the blockchain. Hypothetically, if someone were to manipulate the records of information stored in a block, this person would have to re-calculate the hash values of all subsequent blocks to escape detection. In any other case, the integrity of the data would be violated, and the blockchain be broken.

Blocks of data Resulting hash values

”dataA 0xe35f47d 1 ”

0x60cbc1a87c2c7bad994784ded812af98

”dataA 0xe35f47d 2 ” fMD5(data) 0xb3ae55f566e756efa3af8ebda65d6332 0x0cd26af0131478ae2be6caeead727502

”dataA 0xe35f47d 3 ”

Figure 3: Modifying only one character of a piece of data, e.g., a string, changes that data’s resulting hash value, e.g., 128-bit MD5 hash value, significantly.

Hashes as proof-of-work

Calculating a hash value with the respective hash function does not require a lot of computational resources. Anybody with access to the data of the blockchain described so far could manipulate the records of information in one or several

5 blocks and re-calculate the hash values of all subsequent blocks in the sequence. To prevent such data manipulation, blockchain technology employs a mechanism, which demands that the hash value of a block be below a specified target value. By introducing such a difficulty level, the mechanism imposes a significant computa- tional effort on whoever wants to add a block to the blockchain. The desired hash value can only be found with brute force by continuously changing the block’s data: For this purpose, a nonce is added to the block header every time the hash value is being calculated. This process is repeated until it produces a hash value smaller than the difficulty level. The nonce that produced the acceptable hash value then remains part of the block header as seen in Figure4.

block header : block header : block header : [...] [...] [...] fHash(Block #0) fHash(Block #1) nonce1 nonce2

data section : data section : data section : dataA dataB dataC

Block #0 Block #1 Block #2

Figure 4: The nonce is part of the block header. As such it determines the resulting hash value of the block.

Hence, finding a nonce that yields a hash value below the difficulty level poses a severe computational problem, which can only be solved with brute force. Anybody, who wants to find the acceptable hash value to add a new block to the blockchain, has to commit extensive computational resources to this work. The result of such computational efforts is called proof-of-work [51, p. 6]: The nonce and the resulting hash value below the specified difficulty level are proof of someone’s computational work. The mechanism or process of solving the proof-of-work is called mining. Blockchain users, who commit computational resources to solve

6 the proof-of-work for a block, are called miners. To put this work in perspective: From February 2018 until November 2019, the average daily hash rate for a block on the Ethereum blockchain used to be around 250,000 Gigahashes per second (250k GH/s)2. A powerful GPU, e.g., the GeForce GTX 1080 Ti, is capable of calculating 31.3 · 106 hashes per second (31.3 MH/s or 0.0313 GH/s)3. A miner who solves the proof-of-work in 10 seconds, while the specific block difficulty is approximately 3.13 · 1015, has to commit computing equipment capable of a hash rate of 313,000,000 MH/s hashes per second (313k GH/s). That is 10 million times the hash power of the GTX 1080. From this follows, that the mining mechanism disproportionately impedes the insertion of a new block in the middle of the blockchain or the manipulation of the records of information in existing blocks, as the proof-of-work would have to be solved for each subsequent block. Moreover, computing power translates into electrical energy consumption, which puts a real cost burden on miners, and therefore discourages them from committing their computational resources to data manipulation.

Proof-of-work as a consensus mechanism in a decentralized network

The proof-of-work also serves as a consensus mechanism: A blockchain is not stored on a centralized server like a database within a client-server architecture as shown in Figure5.

2Cfr. etherscan.io/chart/hashrate. (Last visited February 2, 2019.) 3Cfr. www.techspot.com/article/1438-ethereum-mining-gpu-benchma rk/. (Last visited February 2, 2019.)

7 Figure 5: Centralized network based Figure 6: Decentralized peer-to-peer on the client-server architecture as it is network as it is used for blockchain tech- used for databases and web services. nology.

Blockchain technology demands the generation of a decentralized organization as shown in Figure6, i.e., a peer-to-peer system consisting of devices, which support the same blockchain protocol, the same processes. These processes transform configured devices into network nodes. Interaction between the nodes is symmetric: Each node acts simultaneously as a client and a server [47]. The blockchain network distinguishes between full nodes and light nodes. Full nodes store a complete copy of the blockchain, while light nodes only download the block headers. The mining process requires that each miner host a copy of the entire blockchain on their node, i.e., they have to run a full node. There is no trust between the nodes. Hence, the consensus mechanism, i.e., the proof-of-work, allows nodes to verify the blockchain data they receive from other nodes. If a node has validated a newly created record of information or change to the blockchain, i.e., the proof-of-work for a newly added block, the node will then propagate the data further to other nodes. Each node can decide for itself, whether the received data is valid or not. The final decision on which block is appended to the blockchain is the result of comparing data with other nodes. The information about verified blocks has to be spread over the network.

Broadcasting information in a peer-to-peer network

To this end, two types of data have to be broadcasted on the blockchain’s peer-

8 to-peer network: (1) newly created records of information, and (2) the resulting changes to the blockchain. Full nodes redistribute information about changes to the blockchain as well as newly created data, i.e., records of information, on the network, while light nodes may serve as endpoints that only broadcast new records of information created by their owners.

Miners decide, which records of information they want to include in the block they are about to mine: They collect the new records of information broadcasted to the network into new blocks, which they then try to mine so that they may be added to the blockchain. Thus, there is some incentive for a rogue miner not to propagate, i.e., broadcast, newly created records of information and collect them in a new block to be mined in secrecy. However, as even endpoint nodes broadcast to more than one node, such behavior would only work with records of information created by the rogue miner. In the end, such behavior would be futile if the rogue miner did not also have the resources to finish the proof-of-work before anybody else’s proof-of-work.

Incentive for sustaining the blockchain network

With the respective blockchain software and adequate hardware, the blockchain network is accessible to anyone. While light nodes may run on less powerful devices, miners require powerful computing equipment to solve the proof-of-work and add new blocks to the blockchain. Hence, miners continuously compete with each other over the proof-of-work for the next block in the sequence.

The reason why miners commit their computing power in the first place is that blockchain technology is intrinsically linked to cryptocurrencies, whose value today is pegged to real-world fiat currencies. A blockchain serves as a public ledger, which stores the transactions between accounts. The transactions are the records of information, which miners group in blocks. Accounts are identified by hexadecimal numbers, i.e., the account’s address. Transactions describe monetary value transfers from one address to another, i.e., from one account to another account. The real-world value of such transfers depends on the exchange rate between the blockchain’s cryptocurrency and other real-world fiat currencies, e.g.,

9 the US Dollar or the Euro. The miner, who first solves the proof-of-work for a new block, receives compensation in the blockchain’s respective currency, which has real-world value. In addition, senders pay a small fee for each transaction they send to another account. Miners thus use their hash power to compete against all the other miners on the blockchain network.

1.2 Ethereum

1.2.1 Ethereum from Different Perspectives

Ethereum can be viewed from three different perspectives: (1) from a theoretical point of view as a whole, (2) according to its implementation, and (3) its practical application and meaning.

Blockchain as a transaction-based state machine

The Yellow Paper [51] states that Ethereum as a whole is a transaction-based state machine as shown in Figure7. From this perspective, Ethereum is defined through a so-called world state, formally denoted as σ. As shown in Figure7, only valid transactions can cause the world state to transition from one state to another: e.g.,

σt −→ σt+1.

10 T : transaction σ : world state

T1, T2 T3

σt σt+1 σt+2

Figure 7: Ethereum can be considered as a transaction-based state machine [51], which changes from one state to another. The transitions of the world state σ are caused by valid transactions.

Blockchain as a record of state-changing causes

From an implementation perspective, however, transactions as such do not change the world state. Transactions have to be grouped into a block first. Only valid transactions of a valid block4 cause a change in the world state. From this perspec- tive, Ethereum is a sequence of blocks chained together through the backward hash reference as shown in Figure8. The blocks contain the records of the causes for change in state.

4The blockchain is a sequence of valid blocks. However, there are different types of blocks, which are part of the Ethereum blockchain [2]: The first block in a blockchain is called the (1) genesis block. Valid blocks are appended to valid blocks, starting with the genesis block. However, not all block become canonical blocks. Miners must cease working on a block as soon as the network has validated another block. An unfinished block is called a (2) stale block, i.e., a block that had to be discarded by the miner because a competing miner found the proof-of-work for the next block in the blockchain first. In the case that two competing miners finish almost at the same time, the finished but rejected block becomes an (3) uncle block, also referred to as ommer block [51]. On the Ethereum blockchain, miners receive rewards for both, valid blocks and uncle blocks. A parallel chain of uncle blocks can grow substantially if parts of the peer-to-peer network believe it to be the canonical chain. For the same reason, these parallel chains can be split up as well.

11 B : Block T : Transaction σ : World state Bb Bb+1

fHash(Bb−1) fHash(Bb)

T1 T3 T2

σt σt+1 σt+2

Figure 8: Transactions are grouped in blocks. On the block-level, transitions of the world state are caused through the addition of finalized blocks to the blockchain [51]. Each block can contain a series of transactions. Child block Bb+1 is linked to its parent block Bb because the child block contains the hash, e.g., fHash(Bb), of the parent block in its block header. This linkage is depicted by the arrow, connecting the two blocks.

Blockchain as a ledger

In practical terms, Ethereum constitutes a ledger composed of all valid transactions between accounts grouped in blocks as shown in Figure9. The blocks can be viewed as the pages of a ledger, on which the transactions contained in a block are recorded.

12 B: Block

Bb+2

Bb+1

Bb

Figure 9: The Ethereum blockchain viewed as a ledger. With each validated block the ledger’s ’volume’ increases, containing the record of every valid transaction.

1.2.2 Ethereum World State σ

Mapping between addresses and account states

The world state σ [51] contains all Ethereum accounts as objects. An account is the mapping of an address a to the state of the account, i.e., the account state. Thus, each state σt of the world state maps all Ethereum addresses to their respective account states. The account state is denoted as the tuple σ[a] and contains four

fields as shown in Figure 10: (1) nonce (σ[a]n), (2) balance (σ[a]b), (3) storageRoot

(σ[a]s), and (4) codeHash (σ[a]). The nonce is a scalar value, which counts the transactions sent from this account’s address. The balance is a scalar value, which represents the accrued amount of money owned by the address. While Ether is Ethereum’s native currency, the balance is calculated in Wei, the smallest unit of Ethereum’s currency. One Ether is equivalent to 1018 Wei. The storageRoot is a 256-bit hash5 value of the data stored in the account storage in the Ethereum state database. The codeHash is the 256-bit hash value of the bytecode that belongs to the account, and which is stored in the Ethereum state database. Bytecode on the Ethereum blockchain is immutable. Hence, the value of the codeHash field will never change.

5Hash here refers to the Keccak 256-bit hash used on Ethereum.

13 World state σ

State database

Address a Account state σ[a] Storage

160-bit identifier nonce

balance Bytecode storageRoot 6060...

codeHash

Figure 10: The world state σ maps an address to an account state. While the transactions that lead to some world state σt are stored in the respective blocks on the blockchain, the data structure for the mapping of addresses to account states, as well as the account’s storage and bytecode, is stored in the Ethereum state database [51].

Data structure of the Ethereum state database

The data structure of the Ethereum state database constitutes a Merkle Patricia tree [13][29], a combination of a (1) Merkle tree and a (2) Radix tree. A Merkle tree builds up from the leave nodes at the bottom via intermediate nodes to the root node at the top: The leave nodes at the bottom contain the data. The intermediate nodes contain the hash of their child nodes. Child nodes are either intermediate nodes or leave nodes. The root node at the top also contains the hash of its child nodes, which are intermediate nodes. The hash stored in the root node of the Merkle tree is called the root hash. The radix tree is built on top of many Merkle trees. The leave nodes of the radix tree represent the root nodes of the Merkle trees. The root node of the radix tree and its intermediate nodes contain each a single character from these Merkle tree root hashes. Following down the path of a radix

14 tree, while searching for a specific Merkle tree root hash, leads to the root hash of the corresponding Merkle tree.

The root hash of a Merkle tree for the state database is contained in each block [51]. In this way, the world state and the blockchain are cryptographically linked to each other. The data at the bottom of the data structure is thus verifiable for each individual state because the respective block, which caused the state, also contains that state’s root hash.

The hash algorithm that is used in Ethereum is a 256-bit Keccak hash, also referred to as SHA3 hash [51]. Furthermore, all data in Ethereum is serialized by the data format RLP (Recursive Length Prefix) [22]. Data can be either a string (byte array) or a byte array of byte arrays (strings).

1.2.3 Ethereum Account Types

From this look at the Ethereum account state, it becomes evident that Ethereum, viewed from its practical aspects, is more than a ledger that records transactions between accounts. Ethereum supports a decentralized computer: The Ethereum virtual machine (EVM) allows for the execution of bytecode. Accounts can own EVM bytecode, which is stored in the Ethereum state database. A transaction addressed to an account, which owns EVM bytecode, triggers the execution of that EVM bytecode. To this end, Ethereum supports two types of accounts [17]: (1) non-contract accounts, and (2) contract accounts. A non-contract account is an externally owned account (EOA). Access to a non-contract account is controlled by a private key. Non-contract accounts do not contain EVM bytecode. Therefore, the fields storageRoot and codeHash remain empty as shown in Figure 11. By contrast, as shown in Figure 12, a contract account is publically available and contains EVM bytecode, which can be executed on the Ethereum virtual machine (EVM). Hence, a contract account is controlled by its EVM bytecode, while bytecode execution is triggered by transactions sent to the contract account.

15 Address Account state Address Account state Storage

160-bit identifier nonce 160-bit identifier nonce

balance balance Bytecode storageRoot storageRoot 6060...

codeHash codeHash

Figure 11: Externally owned account Figure 12: Contract account (CA) with (EOA). An EOA does not own EVM EVM bytecode and storage for contract bytecode. data.

1.2.4 Ethereum Transactions

Transactions

Only owners of non-contract accounts (EOA) can send transactions from their account’s address to the address of other accounts, including contract accounts (CA). Sending transactions to another account’s address allows for transfer of value, i.e., Ether, to the respective account. Transactions sent from a non-contract account’s address to the address of a contract account cause the execution of the EVM bytecode, such an account owns.

Fees (Gas)

The processing of transactions, i.e., the mining of blocks, requires computing power, which translates into real-world costs for electrical power. Therefore, Senders are charged a fee for their transactions. The fee is credited to the account of the miner, who first delivers the proof-of-work [2]. On Ethereum, fees for computing power are charged in gas6. The minimum fee required for a transaction to be processed at all is 21,000 gas. The price of gas in Wei for processing a

6If Ethereum were an engine, then transaction fees would be the fuel, i.e., the gas, that power it [17].

16 transaction depends on how much senders are willing to pay7, as well as the miners’ price preferences. Miners, who have more hashing power, also have higher ask prices. Hence, senders can incentivize miners to process their transactions more rapidly by simply increasing their price offer for gas [44].

The amount of gas purchased for transactions to addresses of contract accounts is a more subtle calculation, as the sender of that transaction has to pay a fee for code execution as well. However, different EVM bytecode instructions consume varying amounts of gas [17]. Senders who want EVM bytecode to be executed on the Ethereum blockchain must, therefore, purchase a sufficient amount of gas with their transaction. Any Ether for unused units of gas is refundable, and it is possible to set a limit for how much gas can be purchased in one transaction. However, should the amount of purchased gas not be sufficient, then the transaction is reverted. Used gas is non-refundable. In any case, there is an upper limit as to how much gas can be purchased in one transaction8.

The price for gas (in Wei) that needs to be paid for transactions, which result in EVM bytecode execution, depends on the amount of gas, each EVM bytecode instruction consumes. Transaction senders pay the miners in Ether (Wei) for the gas, their transactions consume during EVM bytecode execution. While the amount of gas to be paid depends on the type of EVM bytecode instruction that is being executed, and, therefore, is fixed, the price for gas depends on a free market economy [17]: Miners decide which transactions they want to mine, and at what price they sell their gas. They can refuse to mine transactions from senders that offer gas prices below their minimum acceptance level. In Ethereum, the price for gas determines how fast a transaction is being processed [44]. Senders, on the other hand, decide how much they are willing to pay for gas, i.e., for faster processing of their transactions.

7At the time of writing, the average minimum price for one gas varied between 2 · 109 Wei (2 Gwei) and 3 · 109 Wei (3 Gwei), which, in fiat currency, amounts to a transaction fee of $0.006 and $0.009, respectively, at slower processing times [44]. 8The upper limit ofgas for one transaction ensures that any EVM bytecode that is executed on the Ethereum virtual machine will terminate; either on its own or because the transaction did not purchase enough gas, and EVM bytecode execution ran out of gas.

17 Types of transactions and transaction fields

According to the Yellow Paper [51], there are two types of transactions: (1) transactions that result in a message call from a non-contract account (EOA) to another account, e.g., contract (CA) or non-contract account (EOA), and (2) transactions that result in the creation of a new contract account, i.e., contract creation. As transactions originate outside the blockchain and the world state (state database), transactions must be signed with the private key of the EOA’s address, the transactions are being sent from. Figure 13 shows how different transactions affect the world state: (1) The owner of EOA1 sends a transaction from the address of EOA1 (address1) to the address of EOA2 (address2). The transaction is signed with the private key belonging to address1. As a result, when the transaction is executed, a message call is sent from EOA1 to EOA2. Transactions between non-contract accounts are used to transfer value, i.e., Ether. (2) Another signed transaction is sent from the address of EOA1 (address1); this time to a the address of the contract account CA3 (address3. This transaction results in a message call from EOA1 to CA3. Subsequently, the EVM bytecode owned by CA3 is executed. In addition, it is possible to transfer value to a contract this way. (3) A signed transaction, which is sent from the address of EOA1 (address1) without specifying the recipient’s address (null address), results in the subsequent contract creation of

CA4.

18 Signed transaction Tfrom address1

World state σt World state σt+1

Message call 1 Address2 EOA2 EOA1 EOA2

Value transfer

Message call 2 Address3 CA3 EOA1 CA3

Bytecode execution Value transfer

3 Null address EOA1 CA4

Contract creation

Figure 13: (1) Signed transaction resulting in a message call from the sender’s account (EOA1)to EOA2, and a value transfer from EOA1 to EOA2. (2) Signed transaction resulting in a message call from the sender’s account (EOA1) to CA3, and subsequent EVM bytecode execution, as well as possible value transfer from EOA1 to CA3. (3) Signed transaction resulting in the subsequent contract creation of CA4. Note: The world state σt+1 here does not refer to the final state, but to a substate, in which message calls and contract creation are executed. The final state only contains the result of these message calls as well as the newly created contracts.

Transaction fields

Therefore, sending a valid transaction requires several fields of information as shown in Figure 14. Both transactions, those resulting in message calls and those resulting in contract creation, require six common fields, which are defined as follows [51]: (1) The nonce is the number of transactions sent from the address of a non-contract account (EOA). (2) The gasPrice refers to the monetary value in Wei the sender of the transaction is willing to pay per unit of gas. (3) The gasLimit refers to the maximum units of gas, the sender wants to purchase for the transaction. The price for gas is paid up-front, and only unused gas is refundable.

19 (4) The to field requires the account address (160-bit identifier) of the recipient if the transaction is to result in a message call to either another EOA or a CA. By contrast, for contract creation, the to field must remain empty. As a result, a new contract account is created, and its address is returned. (5) The value field contains the monetary value defined in Wei, which is to be credited to the recipient’s account. (6) The values v, r, and s are the result of ECDSA [6] signing the transaction with the private key of the sender’s account address9. Ethereum transactions do not contain a from field because the sender’s account address is recoverable from the outputs v, r, and s10. Transactions for the purpose of contract creation further use (7) the init field, which is an unlimited size byte array containing (a) the contract’s loader code (constructor) as well as (b) the contract’s EVM runtime bytecode (body). The loader code is executed only once at contract creation and returns the contract’s EVM bytecode11, which is stored in the Ethereum state database [49]. Each message call sent to this contract afterwards triggers the execution of the contract’s EVM bytecode. Transactions, which cause such message calls, use (8) the data field, an unlimited size byte array. The payload of the data field consists of function-identifying data and the respective parameter values, and is interpreted as bytecode to be executed by the EVM.

9Cfr. github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md. (Last vis- ited March 10, 2019.) 10The recovery of the sender’s account address is described formally in Appendix F of the Yellow Paper [51]. 11This code is often referred to as EVM runtime bytecode.

20 Transaction T

1 nonce

2 gasPrice Empty in contract creation transaction. 3 gasLimit

4 to

5 value Used in contract creation transaction. 6 v, r, s Used in message call transactions. 7 init

8 data

Figure 14: An Ethereum transaction T consists of six fields, common to both, contract creation and message call transactions, and two fields (gray) that are specific to contract creation and message call transactions, respectively.

Transaction execution

Transactions sent from non-contract accounts change the world state σ. As this change cannot be reverted, transactions need to be validated first [51]: (1) A transaction must be a well-formed recursive length prefix (RLP). (2) Transactions must have a valid signature, i.e., the recovered address from the ECDSA signature must be a valid address. (3) The nonce of the transaction must be the same as that of the sender’s account state. (4) The gasLimit must be at least equal to the minimum amount of gas required for a transaction to a non-contract account, e.g., 21,000 gas. (5) The balance of the sender’s account state must own sufficient funds to cover at least the up-front payment for purchasing the 21.000 units of gas necessary for any transaction sent from a non-contract account. After successful validation, a transaction is resolved according to the contents of its fields either to a message call or contract creation. Successfully executed transactions are stored on the blockchain, while the resulting changes, they caused, are stored in the state database12. 12Each block on the Ethereum blockchain contains the hash of the root of the state database, i.e. the world state σt+1 resulting from the execution of all the transactions grouped in the respective block.

21 Message call and contract creation

Valid transactions result either in message calls between account states or in contract creation as seen in Figure 13, both of which require a set of parameters for execution. For a message call, execution needs the following parameters [51]: (1) the sender, i.e., the address of the account, from which the message call originates13; (2) the transaction originator, i.e., the address of the non-contract account (EOA), from which the transaction originated, and which is retrieved from the transaction’s ECDSA signature; (3) the recipient, i.e., the address of the account, to which the message call is being sent; (4) the address of the contract account14, whose EVM bytecode is to be executed; (5) available gas; (6) the gas price; (7) the value that is being transferred; (8) input data, which is a byte array of arbitrary length; (9) the current depth of stack of message calls and contract creations; (10) the permissions to modify the world state. A message call either results in value transfer, EVM bytecode execution or both. Additionally, in the case of EVM bytecode execution, a message call can result in further message calls or additional contract creation. Message calls, which directly result from a transaction, are called top-level message calls, while message calls resulting from a top-level message call are called inner message calls [23]. Message calls are not stored in the state database because they are the deterministic result of executing transactions. The resulting final world state, however, is stored in the state database.

For contract creation, the following parameters are needed [51]: (1) the sender, (2) the transaction originator, (3) available gas, (4) gas price, (5) endowment, i.e., value, (6) a byte array of arbitrary length with the new contract’s initialization bytecode (loader code), (7) the current depth of stack of message calls and contract creations, and (8) the permissions to modify the world state. Contract creation determines the address of the new contract account, sets the nonce of the contract account to one, transfers any value (endowment) to the account’s balance, sets its storage to empty, and initializes the account by executing the loader code (constructor), which returns the contract’s EVM bytecode (body) that is stored

13The sender of a message call is not always the transaction orginator, e.g. when a message call is sent from a CA to another CA or an EOA. 14In most cases, this address of the contract account is the same as the recipient.

22 in the state database. The corresponding hashes to the contract’s storage data and the EVM bytecode, storageRoot and codeHash respectively, are stored in the contract’s account state as well. Code execution can result in further message calls or additional contract creation. Again, as contract creation is the result of executing a transaction, only the new contract as part of the resulting final world state is stored in the state database.

1.2.5 Ethereum Virtual Machine (EVM)

The Ethereum virtual machine (EVM) as shown in Figure 15 executes the bytecode owned by a contract account (CA). The EVM is a simple stack machine, which consists of (1) a stack, (2) memory (RAM), and (3) a program counter for the RAM. (4) The account storage, which is located in the state database, contains generally-accessible persistent contract data. The location, where the EVM byte- code resides in the state database, serves as (5) a virtual ROM for the EVM, from which it is loaded into RAM. Bytecode executed in the EVM can only manipu- late the stack, the memory, and the account storage. The word size of stack and memory is 256 bits (32 bytes). While the stack is limited to 1024 words, memory is an infinitely expandable word-addressed byte array15. The account storage is a word-addressed word-array, which contains key-value pairs. Key and value each have a word size of 256 bit.

Machine state µ

Similar to the world state σ, which maps addresses to account states, there is a state, which keeps track of the volatile aspects of the EVM: the machine state. The

15While in storage a word-sized (256-bit) key addresses a word-size value, in memory, a word- sized address points to a single byte. Reading from memory is word-sized, while it is possible to write either 256-bit or 8-bit values to memory. However, memory expansion can only be achieved in word-sized steps. Writing to a higher address in memory will first expand the memory in word-sized steps until the called address is included. Then, memory is expanded further until sufficient word-sized space is allocated to write the data to memory. Each step of word-sized memory allocation, as well as writing to memory costs gas.

23 machine state is defined by the tuple µ, which includes (1) the available gas (g), (2) the program counter (pc), (3) the contents of RAM (m), (4) the active number of words in RAM (i), and (5) the contents of the stack (s).

As the execution of EVM bytecode instructions consumes gas, the machine state µ tracks the change in available gas: g −→ g0. Code execution stops if the program counter reaches the end of the EVM bytecode in RAM. Exceptional halting only occurs if either there is insufficient gas16 available, or the program experiences an exception due to unusual stack behavior. When execution stops, the machine state µ has transitioned to µ0.

Ethereum execution environment

17 The EVM’s execution environment computes the transition σt −→ σt+1 , which depends on the machine state’s transition µ −→ µ0, as well as the value of the 0 remaining gas g . Hence, in order to compute σt+1, the execution environment has to know the world state σ, the machine state µ, and the available gas g provided by the transaction. The execution environment also needs to be provided with additional information, which is defined by the tuple I18.

16Ethereum’s gas limit ensures that code execution always terminates, i.e., infinite loops are impossible by design [51]. 17 0 In the Yellow Paper [51], the resulting world state σt+1 is denoted as σ . 18 Ia: the address of the account that owns the code to be executed; Io: the sender address of the transaction that triggered the code execution; Ip: the price of gas, which may vary and determines the gas available for code execution; Id: the byte array that contains the input data for the code execution; Is: the account address that caused the code execution, which may not be the same as Io; Iv: the monetary value, in Wei, sent with the transaction; Ib: the byte array with the byte code that is to be executed; IH : the block header of the current block; Ie: a number that states how many contract accounts are being called for execution or how many contract account creations are to be executed; Iw: the necessary permission to make modifications to the state, e.g., to the account storage.

24 Execution environment I

World state σ

EVM State database

Machine state µ Account storage Counter (pc) (RAM) Stack i RAM

EVM bytecode (ROM)

Gas 6060... 6060...

Figure 15: Ethereum virtual machine (EVM) [51]. The contract’s persistent storage and read-only EVM bytecode are physically located in the Ethereum state database. However, during contract execution, both, the storage and the EVM bytecode constitute logical components of the EVM. Before execution, the EVM bytecode is loaded into memory (RAM). Input data for the contract’s EVM bytecode is loaded either on the stack or into memory. The world state σ, the machine state µ, the available gas and the necessary additional information I constitute the execution environment, where the bytecode execution is performed.

25 1.2.6 Ethereum Peer-to-Peer Network

The Ethereum peer-to-peer network is comprised of a set of network protocols referred to as devp2p [15]. Users run Ethereum implementations 19 on their physical machines. The software implements devp2p and turns user machines into peer-to-peer network nodes. In addition, the software is an implementation of the formal specifications for the Ethereum protocol20 as defined in the Yellow Paper [51]. Thus, machines running an Ethereum implementation turn into Ethereum peer-to-peer nodes as shown in Figure 16: (1) Ethereum full nodes host a copy of the entire blockchain and the Ethereum state database, which represents the current world state σ. Full nodes verify blocks and transactions, and relay them to the network [40]. Miners are required to run full nodes. (2) Ethereum light nodes [21], on the other hand, synchronize with full nodes and download the latest blockchain’s block headers21. As light nodes do not host a copy of the state database, i.e., the world state, they retrieve data from full nodes on demand. Additionally, light nodes require full nodes to relay the transactions their users send to the Ethereum network [40].

19An Ethereum implementation refers to an official implementation of the Ethereum protocol [18]. Such software allows users to turn their machines into Ethereum nodes, as well as to connect to their node with an Ethereum client instance. Ethereum implementations are usually referred to as Ethereum clients. 20By contrast, the present work has explained these specifications in a descriptive manner. 21Current Ethereum implementations have a list of trusted full node peers built into their code [40]. Hence, users have to trust the developers, who created the software.

26 Full node

Light node World state σ P2P connection

World state σ

World state σ User

World state σ P2P connection

Figure 16: Ethereum peer-to-peer network comprised of decentralized nodes: (1) full nodes, which host complete copies of both, the Ethereum state database (world state σ) and the Ethereum blockchain, and (2) light nodes, which depend on full nodes for downloading blockchain information, e.g. block headers and world state data. In addition, light nodes depend on full nodes to replay transactions, sent from light nodes, to other full nodes in the Ethereum peer-to-peer network. Ethereum nodes communicate with each other over a P2P connection, provided by Ethereum implementations running on physical machines.

Ethereum implementations

Users can choose from various Ethereum implementations. Some implementations allow users to turn their machines into Ethereum nodes, and provide Ethereum clients, which allow users to connect to their local Ethereum nodes, e.g., Geth or Parity. Other implementations, such as Mist, Metamask, the Remix IDE or Truffle only provide Ethereum clients. Most clients use the JavaScript Web3 API [19] to exchange data with their local nodes via the JSON RPC protocol22, i.e., JSON remote procedure calls [20]. Ethereum nodes are part of the Ethereum peer-to-peer network, where they exchange data via P2P connections as seen in Figure 17.

22The JavaScript Web3 API (JavaScript API [19]) is a wrapper around the JSON RPC API [20].

27 JSON RPC connection

P2P connection Ethereum peer-to-peer network

User machine

Client Node

World state σ Web3 API Web3 API

devp2p User machine User machine User machine

Client Node Client Node Client Node

World state σ World state σ World state σ Web3 API Web3 API Web3 API Web3 API Web3 API Web3 API Operation system devp2p devp2p devp2p

Operation system Operation system Operation system

Figure 17: Most Ethereum clients connect to a local Ethereum node on a user machine via the JSON RPC protocol, using either the JSON RPC API [20] or the JavaScript Web3 API [19]. Ethereum nodes, whether full nodes, hosting complete copies of both, the state database and the Ethereum blockchain, or light nodes, which only download such data on demand, connect with each other via a P2P connection. All Ethereum nodes as a whole constitute the decentralized Ethereum peer-to-peer network.

1.3 Smart Contracts

Contract accounts that own EVM bytecode are also called smart contracts. This part of the present work highlights the link between source code, EVM bytecode and assembly code (opcode).

28 1.3.1 Smart Contracts at EVM Bytecode Level

Solidity source code

Ethereum smart contracts23 are programs written in the high-level programming language Solidity, which must be compiled to EVM bytecode for deployment and execution. Listing1 shows example source code of a contract, which was created with the browser version of the Remix IDE. Solidity’s syntax is similar to ECMAScript, i.e., JavaScript. However, the language has been purposefully adapted to the 256-bit architecture of the EVM stack-based machine.

pragma solidity ^0.5.5;

contract AddContract{

uint256 public result;

function addNumbers(uint256 a, uint b) public { result=a+b; } } Listing 1: Example of Solidity code.

EVM bytecode

Solidity source code must be compiled to EVM bytecode instructions consisting of byte-sized hexadecimal values [39] in big endian order, i.e., network byte order [51]. EVM bytecode is the machine language, which the EVM processes during execution. The Solidity compiler is called solc, and its installed version corresponds to the version of Solidity the compiler can compile. The source code from Listing1 compiles to the following EVM bytecode instructions shown in Listing2:

23Ethereum is not the only blockchain environment that offers smart contract capabilities. Bitcoin has offered smart contract capabilities from its very beginnings in 2009 [43]. Unlike Ethereum, Bitcoin offers a scripting system called Script, which is not Turing-complete [1].

29 6080604052 34801561001057600080 fd5b5060c78061001f6000396000f3 fe 6080604052 348015600f57600080 fd5b506004361060325760003560e0 1c806365372147146037578063ef9f c50b146053575b600080fd5b603d60 88565b604051808281526020019150 5060405180910390f35b6086600480 36036040811015606757600080fd5b 810190808035906020019092919080 359060200190929190505050608e56 5b005b60005481565b808201600081 905550505056fea165 627a7a72 3058 20ab0685d5291ce6b9b1a1ea3ca99b 6b44a588bd1655f704921a9f64f080 18d2dd0029 Listing 2: The source code from Listing1 compiled to EVM bytecode.

The EVM bytecode from Listing2, which is a continuous string of hexadecimal values, consists of three parts [49] separated for illustration only: (1) The loader code (constructor), which is used at contract initialization. The loader code returns (2) the body, i.e., the contract’s EVM runtime bytecode, which is executed every time, the contract receives a message call. The EVM runtime bytecode is stored in the Ethereum database and is immutable. (3) The Patricia Merkle trie hash, which is used to retrieve data from the storage associated with the contract after initialization. This hash is also called the Swarm hash or bzzhash [24] because of the hash’s magic number, i.e., 0x627a7a72, which is ASCII for bzzr.

Function signatures and semantics

In Ethereum, a function signature refers to the human readable text representation of a Solidity source code function, e.g., addNumbers(uint256,uint256)24 [16]. However, at bytecode level, only the first four bytes of the Keccak256

24Human readable canonical representation of the function signature only uses argument types [16].

30 hash (SHA3) are used to identify functions. These four-byte signatures are called function selectors, e.g., 0xEF9FC50B at (2E) in Listing3. As the input of a hash function cannot be retrieved from the hash function’s output, there is no function to map function selectors back to human readable function signatures25. However, the Ethereum Function Signature Database [16] maps function selectors to human readable function signatures that have been provided by users. Function signatures provide valuable semantic insight for EVM bytecode analysis: (1) Most programming best practices demand that a function’s name should carry the function’s semantics. (2) The type of a function’s arguments provides some information about what a contract considers valid input. Although the database is an essential tool for identifying function signatures, it is by far not complete26. This is apart from the fact that names of functions and variables may not be in English: The function signatures ergebnis() and result() carry the same semantic content, albeit in two different human languages, German and English respectively. However, the corresponding function selectors, 0x529B8E60 and 0x65372147, differ significantly.

EVM bytecode execution

During execution, the EVM bytecode is loaded from the state database into the execution environment, where the EVM bytecode constitutes the EVM’s ROM. The EVM sets the program counter at the beginning of the EVM bytecode and begins to execute each instruction at the current program counter location. As the EVM does not possess registers, input arguments and parameters for instructions are pushed onto the stack. The program counter is incremented after an instruction has been executed. Jumps can change the program counter to any location within the contract’s EVM bytecode, moving it to the respective jump destination. The instruction JUMP [7] moves the program counter to a location in the EVM bytecode. This location is given by the last element pushed onto the stack. The instruction JUMPI [7], i.e., conditional jump, only moves the program counter to the jump destination if some condition is met. The condition is the second to last element 25In any case, a function selector is not a complete hash, but misses the rest of its 224 bits 26Cfr. gist.github.com/holiman/563da876c4ce15629f57ffdc4046383b. (Last visited March 20, 2019.)

31 pushed onto the stack. Not reaching the jump destination causes an exception and the program ends.

Function selectors and the dispatcher function

The message call that triggers a contract’s EVM bytecode execution contains the function selector and possible input values in the input data field. Every contract, whose EVM bytecode offers externally accessible functions, contains an integrated dispatcher function [49], which compares the function selector from the message call’s input data field with the function selectors contained in the smart contract’s EVM bytecode. If the comparison evaluates to true, a conditional jump occurs, which sends the program counter to the jump destination, where the code of the function in question is located. The dispatcher function consists of a comparison between the incoming function selectors and each of the contract’s function selectors, as well as a subsequent conditional jump to the respective function.

Example of EVM bytecode execution with opcode

EVM bytecode can be depicted in human readable form as assembly code, which is referred to as EVM operation code (EVM opcode). The following opcode in Listing3 was obtained with the browser version of the Remix IDE27:

27The example can be followed by copying the Solidity source code into the Remix IDE at remix.ethereum.org. As soon as the code is compiled and deployed to the in- tegrated JavaScript VM blockchain, a transaction can be sent to the contract using the addNumbers(uint256,uint256) function. Choosing the debug option next to the transac- tion log below the source code opens the debugger.

32 1 PUSH1 0x80 PUSH1 0x40 MSTORE 1A CALLVALUE DUP1 ISZERO (=⇒ 1) PUSH2 0x10 (=b 16dec) JUMPI (to 1B) PUSH1 0x0 DUP1 REVERT 1B JUMPDEST (0x10 =b 16dec) POP PUSH1 0xC7 DUP1 PUSH2 0x1F PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID 2 PUSH1 0x80 PUSH1 0x40 MSTORE

2A CALLVALUE DUP1 ISZERO (=⇒ 1) PUSH1 0xF (=b 16dec) JUMPI (to 2B) PUSH1 0x0 DUP1 REVERT 2B JUMPDEST (0xF =b 16dec) POP PUSH1 0x4 CALLDATASIZE LT (=⇒ 0) PUSH1 0x32 =b 50dec) JUMPI (to 2C) 2C PUSH1 0x0 CALLDATALOAD (four-byte function selector from transaction field data) PUSH1 0xE0 (=b 224dec) SHR (Shift right 224 bits) DUP1 2D PUSH4 0x65372147 (four-byte function selector for getter function result()) EQ (=⇒

0) PUSH1 0x37 (=b 55dec) JUMPI (to 2E) 2E DUP1 PUSH4 0xEF9FC50B (four-byte function selector for function addNumbers(uint256,uint256)) EQ (=⇒ 1) PUSH1 0x53 (=b 83dec) JUMPI (to 2F) JUMPDEST (0x32 =b 50dec) PUSH1 0x0 DUP1 REVERT JUMPDEST (0x37 =b 55dec) PUSH1 0x3D PUSH1 0x88 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN 2F JUMPDEST (0x53 =b 83dec) PUSH1 0x86 (=b 134dec) PUSH1 0x4 DUP1 CALLDATASIZE SUB PUSH1 0x40 DUP2 LT (=⇒ 0) ISZERO (=⇒ 1) PUSH1 0x67 (=b 103dec) JUMPI (to 2G) PUSH1 0x0 DUP1 REVERT 2G JUMPDEST (0x67 =b 103dec) DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD (input uint a) SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 DUP1 CALLDATALOAD (input uint b) SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP

PUSH1 0x8E (=b 142dec) JUMP (to 2H) 2I JUMPDEST (0x86 =b 134dec) STOP JUMPDEST PUSH1 0x0 SLOAD DUP2 JUMP 2H JUMPDEST (0x8E =b 142dec) DUP1 DUP3 ADD (adding the input values) PUSH1 0x0 (storage key for variable uint256 result) DUP2 SWAP1 SSTORE (storing the key value pair in storage) POP POP POP JUMP (to 2I) INVALID LOG1 PUSH6 3 0x627A7A72 3058 KECCAK256 0xab MOD DUP6 0xd5 0x29 SHR 0xe6 0xb9 0xb1 LOG1 0xea EXTCODECOPY 0xa9 SWAP12 PUSH12 0x44A588BD1655F704921A9F64 CREATE DUP1 XOR 0xd2 0xdd STOP 0x29

Listing 3: EVM opcode with annotations corresponding to the EVM bytecode from Listing2. (1) Beginning of the contract’s loader code, which is only executed once during contract creation. (2) Beginning of the contract’s EVM runtime bytecode, which is executed every time the contract receives a message call. (3) The bzzhash.

The EVM opcode debugger integrated in the Remix IDE can be used to replicate addresses for jump destinations (JUMPDEST) as they are shown in Listing3. The Remix IDE allows programmers (1) to deploy contract source code either to the blockchain of the integrated JavaScript VM, thus creating a contract, and (2) interact with the contract (CA), by sending transactions from the JavaScript VM’s EOA addresses to the contract’s address. As these transactions result in message calls from the JavaScript VM’s EOA to the CA, the contract’s EVM bytecode is executed. Transactions that lead to EVM bytecode execution can be analyzed with Remix’s integrated debugger. The debugger can step through the contract’s opcode, given the contents of a transaction’s data field28. Using a transaction that called the contract’s addNumbers(uint256 a,uint256 b) function with two inputs values in the Remix IDE, and stepping through the human readable opcode, the

28The contents of the transaction’s data field correspond to the resulting message call’s input data field.

33 Remix debugger shows which values the EVM execution environment knows at each instruction, e.g., on the stack, in memory or in storage.

Bytecode execution first evaluates the value field, checking for a possible value transfer to the contract as seen at (2A) in Listing3. However, the entry point to the contract’s EVM bytecode, the contract’s functions, consists in the dispatcher func- tion [49]: At (2B), the size of the input data field is compared with, which evaluates to false, zero. At (2C) in Listing3, the function selector 0xEF9FC50B, sent with the message call’s input data field, is put onto the stack using the CALLDATALOAD instruction. To ensure, only the four-byte (32-bit) function selector is on the stack, the 256-bit word is shifted 224 bits to the right (SHR). At (2D) and (2E), the dispatcher function compares the function selector from the message call at (2C) with the function selectors contained in the contract’s EVM bytecode: (2D) the result() function29 and (2E) the addNumbers(uint256,uint256) func- tion. As the comparison at (2E) evaluates to true, the dispatcher function sends the program counter to the jump destination at (2F), where the program checks the size of the input data. The size of the input data needs to be at least 64 byte (0x40), as two arguments are expected by the addNumbers(uint256,uint256) func- tion, 32 bytes each, i.e., two 256-bit words. As the check, using the instructions LT and ISZERO, evaluates to true, the program counter continues at (2G), where the input data is loaded onto the stack with the CALLDATALOAD instruction. At (2H), the arguments from the input data are added together, and the result is stored in the contract’s storage. At the end of the addNumbers(uint256,uint256) function, the program counter jumps to the return address at (2I) (0x86). This jump destination was pushed onto the stack at the beginning of (2F), entering the addNumbers(uint256,uint256) function. The program stops at the end of (2I).

29The Solidity compiler creates getter functions automatically for public state variables, e.g., result() for uint256 public result.

34 1.3.2 Solidity and the Structure of Smart Contracts

The structure of contracts in Solidity is very similar to that of classes in object- oriented languages [23]. Listing4 shows the SimpleStorage.sol contract, which is a modified version of the example from the Solidity documentation [23].

1 pragma solidity ^0.5.5; 2 3 contract SimpleStorage{ 4 uint storedData; 5 6 constructor(uint x) public { 7 storedData=x; 8 } 9 10 function set(uint x) public { 11 storedData=x; 12 } 13 14 function get() public view returns (uint){ 15 return storedData; 16 } 17 } Listing 4: The SimpleStorage.sol contract from the Solidity documentation [23] as a modified version: A public constructor was added, and the version was set to the latest version at the time of writing: v0.5.5.

The contract from Listing4 is written for Solidity version 0.5.5, which, at the time of writing, was the latest version of Solidity [23]. The version changes regu- larly, and so does the syntax. For example, the constructor keyword in line 6 was only introduced in version 0.4.22. Compiling the SimpleStorage.sol contract to EVM bytecode with a compiler for versions ≤ 0.4.21 is not possible.

35 Therefore, developers declare the Solidity version at the top of each smart contract, using the keywords pragma solidity30 as seen in line 1 of Listing4.

Types of contracts

To define a regular smart contract, the keyword contract from line 1 is used, which is similar to class in languages like Java. There are three special types of smart contracts in Solidity, two of which have their own keywords: (1) ab- stract contracts31 (contract), (2) interfaces32 (interface), and (3) libraries (library). For the present work, only libraries are of interest, because regu- lar contracts call library contracts, which provide reusable code for regular contracts.

Contract initialization: the constructor

In line 4 from Listing4, a state variable is declared. State variables are stored in the contract’s storage in the Ethereum state database. The constructor in line 6 is optional. At contract creation, the constructor (loader code) is executed once. The constructor can be used to initialize state variables at contract creation. Uninitialized variables are set to zero.

Visibility of functions and state variables

The constructor in line 6 is accompanied by a visibility type, e.g., public33. In total, there are four visibility types [23]: (1) external, (2) public, (3) private, and (4) internal. The visibility types reflect how function calls

30The keywords pragma solidity are usually combined (1) with a caret (ˆ) to indicate the exact Solidity version, e.g., ^0.5.5, or (2) with inequality symbols, e.g., >=0.5.5 or >=0.5.5 <0.6.0. Note: Not declaring the Solidity version at the top of a contract only results in a compiler warning, but the source code still compiles. 31A contract that contains a function that is not implemented automatically becomes an abstract contract [23]; no additional keyword is necessary to make the distinction. Contracts that inherit from an abstract contract, but do not implement the non-implemented function, become themselves abstract contracts. 32Interfaces [23] cannot (a) implement any functions, (b) inherit from abstract contracts or other interfaces, (c) declare a constructor or state variables. 33Setting the constructor to internal marks the contract as abstract.

36 and state variables relate to transactions. External functions constitute part of the contract’s interface and can be called by contract and non-contract accounts via a message call. However, external functions cannot be called from within the current contract they are defined in, unless the keyword this is used to refer to the current contract, e.g, this.externalFunction(). Public functions also constitute a part of the contract’s interface. Like external functions, public functions can be called from outside the contract. Unlike external functions, public functions can be called from inside the contract they are defined in. Private functions can only be accessed from inside the contract they are defined in. Internal functions can be accessed from inside the contract they are defined in, as well as by derived contracts.

State variables only use three of the four visibility types [23]: (1) public, (2) private, and (3) internal. For public state variables the compiler automati- cally generates a getter function. The function signature is of the same name as the variable, as seen in the above EVM bytecode example with opcodes for Listing 3. Private state variables are only visible within the contract they are defined in. Internal variables can be accessed from inside the contract they are defined in, as well as by derived contracts.

The visibility type private is the most restrictive [23]. Functions and state variables are only accessible by the contract they are defined in. However, visibility types only restrict access rights. In terms of information, functions and state variables are still visible to everybody on the blockchain.

Functions marked as view or pure

EVM bytecode can change the state of a contract’s state variables, whose new values are stored in contract’s storage by the EVM instruction SSTORE34. However, such change of state is not always desired. Functions that are marked view [23], e.g. the getter function in line 14 from Listing4, promise not to change the state of a state variable. Getter functions are automatically marked view. At the EVM

34The SSTORE instruction takes one word-size argument from the stack and is the only EVM bytecode instruction that is allowed to save data to the storage [51].

37 bytecode level, the property view is implemented with the STATICCALL instruc- tion, which guarantees that the state will not be changed. In general, anything that changes the world state is not permitted, including calling functions, which are not marked view. In addition, Solidity functions can be marked with pure [23], promising neither to read nor to change the state of state variables. However, at the EVM bytecode level, pure is implemented with the same STATICCALL instruc- tion as view, which does not guarantee that the state will not be read. Furthermore, functions from other libraries (contracts), which are marked view, are called with DELEGATECALL because there is no instruction that combines STATICCALL and DELGATECALL. Hence, these are only two examples, where Solidity source code promises developers more than the EVM bytecode can guarantee.

Modifiers

Modifiers are another way to change the behavior of functions, e.g., to test whether certain conditions are met before allowing the modified function to execute. Modi- fiers are functions that execute code before, after or around the modified function, which in the modifier function is represented by the underscore statement _; as seen in line 14 from Listing5.

38 1 pragma solidity ^0.5.5; 2 3 contract ModifierExample{ 4 5 address payable owner; 6 uint public counter; 7 8 constructor() public { 9 owner= msg.sender; 10 } 11 12 modifier onlyOwner{ 13 require(msg.sender == owner); 14 _; 15 } 16 17 function setcounter(uint x) public onlyOwner{ 18 counter=x; 19 } 20 } Listing 5: Example of a modifier and a modified function.

The onlyOwner modifier affects the setCounter function in line 17 in such a way that it is only executed after the modifier’s require function in line 13, which checks whether the sender of the message call to the contract is the owner of the contract. Hence, modifiers can be used as sophisticated assertions. An important use case is the payable modifier, which is necessary to credit the contract account with the value (msg.value in Wei) that was sent with the message call.

Payable functions

Contract accounts have a balance, to which users can transfer value. However, value transfer to contract accounts is only possible if the contract contains a function marked payable. Listing6 shows an example of a contract 35, to which users

35This contract is unsafe, as it does not contain any ownership requirements.

39 can transfer value using the deposit function in line 12, which is payable. It is not the deposit function that is being paid: Value transferred to a contract account by calling a payable function is credited to the account’s balance. If a contract is to receive value during contract creation, the constructor must be present and needs to be marked payable as well.

1 pragma solidity ^0.5.5; 2 3 contract Deposit{ 4 5 constructor () payable public { 6 } 7 8 function withdraw() public { 9 msg.sender.transfer(address(this).balance); 10 } 11 12 function deposit() payable public { 13 } 14 15 function getBalance() public view returns ( uint256){ 16 return address(this).balance; 17 } 18 } Listing 6: Example of a contract with a payable function, e.g. deposit() payable public, which allow users to transfer value to the balance of the contract account.

Global variables

Solidity knows several global variables, which can be used in the any scope. The most import ones for the present work are the following: (1) msg with its three members, (1a) msg.sender, (1b) msg.value, and (1c) msg.data. The vari- able msg.sender, as seen in line 9 of Listing6 is of type address payable. (2) An address payable allows access to the balance of the account corre-

40 sponding to the address payable, e.g., msg.sender.balance. Further, (3) an address payable has access to the functions transfer(uint256 amount) and send(uint256 amount) returns (bool), both of which transfer value to the address payable, which calls them, e.g., msg.sender. transfer(address(this).balance) in line 9 from Listin6 transfers the entire balance of the contract account to the sender of the message call. (4) The function selfdestruct(address payable recipient36 destroys the current contract and sends all of its funds to the address payable.

Considering the value many Ethereum smart contracts hold is sufficient incentive for attackers to send a message call, which can execute the selfdestruct function with msg.sender being the final argument.

Return values

Functions that return values need return in the dec- laration as seen in line 14 from Listing4. Solidity functions can return several variables at once, e.g.,

return (a, b) or (success, ouput) = addr.staticcall(data).

Order of elements in function declaration

In summary then, the order of the elements that alter the behavior of a function is as follows:

function + + ([]) + [+ ] [+ ] [+ return ()].

36Formerly known as suicide function, which is deprecated as of Solidity version 0.5.0 [23].

41 Fallback function

The fallback function [23] is an unnamed function, which does not take any arguments, and does not return anything. Every contract can have only one such fallback function, which must be of visibility type external. The fallback function is used, when the dispatcher function cannot match the function selector sent as input data to the contract or if no data was sent at all. A message call that intends to only transfer value to a contract, and does not send any data, thus results in a function call to the contract’s fallback function. However, the fallback function needs to be marked with the payable modifier to only receive a value transfer. Without an existing payable fallback function, execution will throw an exception if the contract receives a message call for a value transfer without input data. Although the fallback function cannot take any arguments, msg.data can be used to access the message call’s input data field [23]. For security reasons, however, the fallback function is restricted to 2,300 gas. Calling external functions from other contracts that consume more gas will cause an out-of-gas exception. Sending Ether to another account or writing to the contract’s storage will also result in an out-of-gas exception.

Remarks

Note that Solidity as a language is evolving rapidly. Had the present work been written in the summer of 2018, the above information would be outdated, and in parts useless. for instance, the latest Solidity compiler is unable to compile source code written in the Solidity version from that time period. The shift from a function of the same name as the contract to theconstructor is but one change without backward compatibility. Developer may have the luxury to write their contracts in the latest version of Solidity. Security analysts, on the other hand, may have to switch between many versions of the Solidity documentation, and need to have access to many versions of the Solidity compiler37. This concludes the description of smart contracts from the EVM bytecode level and the Solidity source code

37py-solc is a Python wrapper around the Solidity compiler solc, which makes such tasks possible. See ??.

42 perspective. Another is the shift to mandatory visibility. The next section will highlight the more vulnerable parts of smart contracts.

1.4 Vulnerability of Smart Contracts

The present work will not repeat the long list of known vulnerabilities at the SWC Registry38 [42] or other lists, such as the one provided by Trail of Bits [37]. Instead, the present work will describe a limited number of EVM bytecode instructions, which enable attackers to exploit smart contracts and steal their funds. The purpose of this emphasis on a few critical instructions is to highlight a minimal working definition of vulnerable smart contracts, which was proposed by Krupp and Rossow in 2018 [31].

1.4.1 Critical Bytecode Instructions in Smart Contracts

(1) If a contract (CA1) sends a message call to another contract (CA2) to execute

CA2’s EVM bytecode, a second EVM instance is created to execute CA2’s EVM bytecode. After execution of CA2’s bytecode, the second EVM intance is cleared, and the first EVM instance continues to execute CA1’s EVM bytecode at the given jump destination, where possible return values from CA2’s EVM bytecode execution can be used as input. (2) If CA1 sends a message call to CA2 to use

CA2’s EVM bytecode as its own code, CA2’s EVM bytecode is loaded to the first

EVM instance’s memory (RAM), where CA1 then can use CA2’s EVM bytecode in CA1’s context.

38SWC Registry: Smart Contract Weakness Classification and Test Cases.

43 In the first case, such a message call is triggered by (1) the CALL instruction [23], in the second case, the message call is triggered by (2) the DELEGATECALL instruction [23]. A third case is (3) the STATICCALL instruction, as seen before, which is like CALL, but reverts if the state is modified in any way. The respective Solidity functions are [23]:

.call(bytes memory) returns (bool, bytes memory)
.delegatecall(bytes memory) returns (bool, bytes memory)
.staticcall(bytes memory) returns (bool, bytes memory)

As attacks on smart contracts do modify the world state, only the first and the second case are of concern to the present work. The important difference between call and delegatecall is the context, in which the EVM bytecode of a contract account is executed [34]: (1) using call, the context of execution is that of the callee, and (2) using delegatecall, the context of execution is that of the caller. State modifications to account storage and account balance are realized in the respective context. An SSTORE instruction used during the execution of delegatecall will store data in the caller’s storage at the location (key) dictated by the called contract’s EVM bytecode.

Contracts calling other accounts with CALL

Aside from calling another contract’s external functions explicitely, the CALL instruction allows a direct value transfer to a given address. The important functions for value transfer, (1) transfer, (2) send, and (3) call39, all compile to the CALL instruction [34]. The CALL instruction, when executed in a contract, is executed in the context of the caller, i.e., the context of the current contract [23]. The stack arguments from top to bottom are [31]: (i) [gas], (ii) [to], (iii) [value], (iv-vii) [in offset], [in size], [out offset], [out size]40.

Thus, control of the second argument on the stack, the recipient (to), while the third stack argument (value) represents a non-zero value, allows attackers to transfer a

39call(bytes memory).value(uint256 amount [23]. 40Values refer to the memory location as well as the length of call data and return data [31].

44 contract’s funds to an account of their choosing. Hence, the recipient (to) can be the address of a contract account41 as well as a non-contract account.

Contracts calling other contracts with DELEGATECALL

The Solidity function delegatecall compiles to the DELEGATECALL instruc- tion. This instruction is used for calls to libraries or regular contracts. The CALL instruction causes the EVM bytecode of the callee to be executed in a separate EVM instance, and the caller receives the return value. The DELEGATECALL instruction, on the other hand, causes the the callee’s EVM bytecode to be copied from the new EVM instance into the caller’s memory (RAM), where it is executed. Further, the values ofmsg.sender and msg.value from delegatecall’s message call do not change. The value of msg.sender, i.e., the calling con- tract’s address, is intrinsically linked to that caller’s account state. Thus, the caller’s context, which comprises the caller’s current address, data storage, and balance, does not change [23]; even if the called contract makes an additional delegatecall42. The stack arguments for the DELEGATECALL are from top to bottom [31] (i) [gas], (ii) [to], (iii-vi) [in offset], [in size], [out offset], [out size].

Hence, attackers, who can control the second stack argument, before the EVM executes the DELEGATECALL instruction, can direct the call to an address of their choosing, and have the calling contract copy arbitrary EVM bytecode from another contract to current contract’s memory (RAM) [31]. Such bytecode injection can be as simple as one assembly command: selfdestruct(attackerAddress)43.

41A contract account would need a payable fallback function to receive value without data. 42There used to be callcode, which compiles to the CALLCODE instruction. CALLCODE would change the context, when more than one ’delegated call’ ( ) was used, e.g., A B C. Using callcode in such a scenario, C would be called in B’s context. 43Solidity assembly command, which would be surrounded by assembly{...} [23].

45 1.4.2 Exploitation of Critical Instructions

Historical example of exploiting delegatecall and the fallback function

In the hack of Parity’s multi-signature wallet, the delegatecall function was abused together with the ability to call the fallback function without providing a valid function selector [33]. The attacker(s) stole 150,037 ETH [27] in the Parity heist, worth approximately US$ 21,000,000 at the time of writing. The wallet’s fallback function44 from Listing7 was called with no value transfer to enter the else if branch in line 5, where the wallet’s library can be called in line 6 via a delegatecall using any data provided by the sender of the message call.

44Cfr. github.com/paritytech/parity-ethereum/blob/4d08e7b0aec46443 bf26547b17d10cb302672835/js/src/contracts/snippets/enhanced-wal let.sol#L424. (Last visited March 19, 2019.)

46 1 // (1) 2 function() payable { 3 if (msg.value > 0) 4 Deposit(msg.sender, msg.value); 5 else if (msg.data.length > 0) 6 _walletLibrary.delegatecall(msg.data); 7 } 8 9 // (2) 10 function initWallet(address[] _owners, uint _required, uint _daylimit){ 11 initDaylimit(_daylimit); 12 initMultiowned(_owners, _required); 13 }

Listing 7: (1) Payable fallback function in Parity’s multi-signature wallet contract, and (2) the initWallet function in the wallet’s ’library’ contract, which is called by the wallet’s constructor [27].

The data, the attacker(s) sent as a payload for the delegatecall in line 6, was the function selector for the initWallet function45, which is part of the wallet’s constructor46 located in the wallet’s library contract. The delegatecall allowed the attacker(s) to execute the initWallet function in the context of the contract under siege, reset the contract’s owner, and thus take possession of the contract’s funds.

45Cfr. github.com/paritytech/parity-ethereum/blob/4d08e7b0aec46443 bf26547b17d10cb302672835/js/src/contracts/snippets/enhanced-wal let.sol#L216. (Last visited March 19, 2019.) 46The Parity multi-signature wallet was written for a version of the Solidity compiler that did not use the constructor keyword, but instead used a function with the same name as the contract. Cfr. github.com/paritytech/parity-ethereum/blob/4d08e7b0a ec46443bf26547b17d10cb302672835/js/src/contracts/snippets/enhance d-wallet.sol#L399. (Last visited March 19, 2019.)

47 Summary of the most critical EVM bytecode instructions

The Parity hack shows how critical the execution of the delegatecall func- tion in the fallback function in line 6 from Listing7 was: The execution of the delegatecall function led to the unwanted code injection of the initWallet function. At bytecode level, the critical instruction is DELEGATECALL [31]. This critical instruction was reachable via the fallback function, which is externally accessible by everyone. Thus, the attacker had a clear path ( ) to the criti- cal instruction: fallback function delegatecall ≡ DELEGATECALL initWallet.

In summary then, the EVM bytecode instructions that are most critical for exploit- ing smart contracts are the following47 [31]: (1) SELDESTRUCT as it allows attackers to directly transfer the funds of the current contract account’s balance to an account of their choosing if they can control the top argument on the stack: the address of the recipient (to).

(2) CALL48 as it allows attackers to directly transfer the funds of the contract account’s balance to an account of their choosing if they have control over the second argument from the stack (to), while the third argument on the stack (value) is non-zero.

(3) DELEGATECALL as it allows for malicious EMV bytecode injection if the attackers can control the second argument from the stack (to).

However, the path is not always as clear as in the Parity example: (1) The access to a critical function may be blocked by one or more state variables [31]. (2) The arguments of a critical function may have to be manipulated before executing the critical function [34]. At bytecode level, the first case requires a manipulation of

47Krupp and Rossow list a fourth instruction, CALLCODE. However, the callcode function is deprecated as of Solidity version 0.5.0 [23]. 48The Solidity documentation [23] warns about using the call function when executing other contracts’ functions because it does not check whether the called function exists, and because it is possible to bypass argument packing. However, send and transfer, which are used for value transfers, also compile to CALL. The Solidity documentation also warns about send and transfer, but only with regards to checking the return values of these functions to guarantee safe Ether transfers [23].

48 the contract’s storage. Only the SSTORE instruction is allowed to save data to the storage, thereby changing state variables. In the second case, e.g., the manipulation of the to argument on the stack for the CALL instruction, the scenario depends on whether the address is provided by a state variable, input data or locally generated value. If the address is stored in a state variable, a suitable SSTORE instruction must be executed first. Thus, the SSTORE instruction is instrumental in clearing the path to some critical instruction [31].

1.4.3 Defining Vulnerable Smart Contracts and Exploits

Krupp and Rossow [31] define the clear path to a critical instruction as a critical path. Thus, (1) a critical path allows the potential execution of one of the critical EVM bytecode instructions highlighted above: (a) SELFDESTRUCT, (b) CALL, and (c) DELEGATECALL. Further, (2) a contract is in a vulnerable state if a message call sent to the contract leads to the execution of the existing critical path. A message call that exploits the vulnerable state defines a critical message call.A critical message call that is caused by a transaction defines a critical transaction.

Not every critical path that exists is immediately reachable via a critical transaction or critical message call. In this case, the contract is not in a vulnerable state. However, a path that contains an SSTORE instruction, which can potentially change the current state from not vulnerable to a vulnerable state defines a state-changing path. A message call that executes a state-changing path defines a state-changing message call. A state-changing message call that is caused be a transaction defines a state-changing transaction. Considered from this point of view, a smart contract is vulnerable if a – possibly empty – sequence of state-changing transactions exists, which result in a vulnerable state of the smart contract [31].

A successful exploit, according to Krupp and Rossow [31], consists of a – possibly empty – sequence of state-changing transactions to a vulnerable contract, which is followed by a critical transaction.

49 2 Towards a Smart Contract Exploit Development Toolchain

The Ethereum community provides many solutions for smart contract development. Web developers can choose from a vast number of online tutorials, and learn how to write, test, and deploy smart contracts. Most tutorials include the necessary steps to set up an appropriate development environment. It is the objective of a smart contract security analysis to test whether a smart contract is vulnerable, and – in the case of a positive result – provide a proof of concept, i.e., a successful exploit. However, a comparison between the toolchain requirements of smart contract developers and those of security analysts will show why tools geared towards contract development cannot meet the requirements of security analysts.

2.1 Requirements Analysis

The toolchain requirements concerning smart contract security analysis go beyond the requirements of contract development as the comparison below intends to highlight. The lack of source code provided on the blockchain only increases the difficulty of meeting the requirements of smart contract security analysis.

50 Toolchain requirements for smart contract development

Developing smart contracts for the Ethereum blockchain creates the following toolchain requirements [11]:

1. Write smart contracts. (IDE) 2. Compile smart contracts. (Compiler) 3. Access Ethereum networks. (Network management) 4. Deploy smart contracts’ source code to test network. (Deployment frame- work) 5. Debug smart contracts. (Debugger) 6. Test smart contract functionality. (Unit testing) 7. Interact with smart contracts. (Console access or IDE) 8. Deploy smart contracts to main network. (Network management)

Toolchain requirements for smart contract security analysis

The toolchain requirements for smart contract security analysis are more differenti- ated49. Not only do the requirements change for manual and automated analysis, but they also depend on the availability of source code. For the majority of smart contracts on the Ethereum blockchain, no source code is provided. The difference between the requirements for development and security analysis becomes more apparent when only EVM bytecode is available.

Manual vulnerability analysis with source code available generates the following requirements: 1. Analyze smart contract’s source code. 2. Compile smart contracts from different Solidity versions50. 3. Access Ethereum networks. 4. Deploy smart contract’s source code to test network. 5. Interact with smart contract to verify vulnerabilities.

49This statement is based on the author’s limited experience with smart contract development and security analysis regarding the use of the tools provided by the Ethereum community. 50The Solidity compiler solc does allow for version control. Only the installed version is available. This is a problem as contracts are written for different versions of Solidity and throw different errors. One solution is to use py-solc, which allows the user to install different versions of solc side by side.

51 Automated vulnerability analysis with source code available generates the follow- ing requirements: 1. Compile smart contracts from different Solidity versions. 2. Automated program analysis of smart contract’s EVM bytecode. 3. Access Ethereum networks. 4. Deploy smart contract’s source code to test network. 5. Interact with smart contract to verify vulnerabilities.

Manual vulnerability analysis with only EVM bytecode available generates the following requirements: 1. Identify function signatures in EVM bytecode. 2. Decompile smart contract’s EVM bytecode. 3. Disassemble smart contract’s EVM bytecode. 4. Analyze decompiled/disassembled smart contract. 5. Access Ethereum networks. 6. Deploy smart contract’s EVM bytecode to test network. 7. Write source code for pseudo smart contract based on function signatures identified in EVM bytecode. 8. Deploy pseudo smart contract’s source code to test network51. 9. Interact with smart contract to verify identified vulnerabilities.

Automated analysis with only EVM bytecode available generates the following requirements: 1. Automated program analysis of smart contract’s EVM bytecode. 2. Access Ethereum networks. 3. Deploy smart contract’s EVM bytecode to test networks. 4. Write source code for pseudo smart contract based on function signatures identified in EVM bytecode. 5. Deploy pseudo smart contract’s source code to test networks. 6. Interact with smart contract to verify vulnerabilities for function signatures and inputs provided by automated analysis.

As the comparison above shows, the security analysis toolchain requirements be- come more demanding, when only the smart contract’s EVM bytecode is available.

51The tool Remix IDE offers the option to deploy the source code of a pseudo contract containing empty functions corresponding to the function signatures of the original EVM bytecode. When provided the address of the original contract, Remix IDE copies the original contract’s EVM bytecode and creates a new contract, using the pseudo contract’s source code to generate the corresponding function and input fields in the GUI.

52 Deploying smart contract EVM bytecode to the blockchain requires the use of com- mand line tools or JavaScripts using the web3.js API. However, as approximately 80 percent of the smart contracts on the Ethereum blockchain do not provide source code, bytecode-only analysis is inevitable. Hence, an appropriate smart contract security analysis toolchain must be capable of (1) deploying EVM bytecode to test networks, (2) manual EVM bytecode analysis, (3) automated EVM bytecode analysis, and (4) exploit development.

2.2 Requirement 1: EVM Bytecode Deployment

Most tools provided by the Ethereum development community do not support the option to create a smart contract with only EVM bytecode provided. Documen- tation and online advice on smart contract creation usually present the use case, where the smart contract’s EVM bytecode and the respective ABI file is required for deployment. The ABI file maps function signatures to function selectors. In most security analysis scenarios, however, this is not an option because the majority of smart contract owners do not provide their source code. The source code, however, is required for generating the smart contract’s ABI file.

There two options to deploy a contract with only the EVM bytecode provided: (1) Metamask52 is an Ethereum wallet allows users to send transactions with data, which is sufficient to send the original creation code (loader and runtime code) to the zero address, i.e., 0x0. (2) The Web3 API allows to send transactions with raw data. The most convenient option is to use Metamask as it also connects to the Remix IDE. This way, the contract can be deployed to the desired test network53. Another possibility would be to create a contract with a contract creation function. The contract in Listing8 shows the source code 54 for a smart contract that can

52The browser application Metamask is available at metamask.io. (Last visited March 17, 2019.) 53The Ethereum test networks are (1) the test network Ropsten (ID 3), (2) the test network Rinkeby (ID 4), and (3) the test network Kovan (ID 42). 54The source code was ported to Solidity version 0.5.5, and reduced to the essential part. The original code is available at gist.github.com/holiman/069de8d056a531575d2 b786df3345665. (Last visited March 19, 2019.)

53 deploy another instance of an existing contract to the blockchain.

1 pragma solidity ^0.5.5; 2 3 contract Cloner{ 4 // gist.github.com/holiman/069 de8d056a531575d2b786df3345665 5 // Original code by Martin Holst Swende 6 // Shortened and ported to Solidity v0.5.5 by Sebastian Kindler 7 address public cloneAddress; 8 9 function clone(address addr) public { 10 address retval; 11 assembly{ 12 // Mulitline split in’mstore’ 13 // for readability only! 14 // See original code for 15 // comment on arguments. 16 mstore(0x0, 17 or (0x588073000000 18 00000000000000 19 00000000000000 20 000000803b8093 21 8091923cF3, 22 mul(addr, 23 0x10000000 24 00000000000))) 25 retval := create(0,0, 32) 26 } 27 cloneAddress= retval; 28 } 29 } Listing 8: Solidity source code for a contract that offers a function to clone an existing address, i.e., deploy another instance of the contract to the same network.

The Solidity assembly code in function clone from Listing8 generates a loader

54 code, to which it adds EVM bytecode for retrieving the target contract’s EVM bytecode from the specified address. The entire code is stored at address 0x0 in the memory (RAM) of the current contract using MSTORE. CREATE executes the code from memory, and initializes a ’clone’ of the target contract. This code could be used to create a contract, which can be given the target contract’s EVM bytecode directly, thus allowing the deployment of EVM bytecode from another Ethereum network.

2.3 Requirement 2: Manual EVM Bytecode Analysis (Tools)

Manual EVM bytecode analysis is a necessity in smart contract security analysis because for most contracts there is no source code available [26]. Having to resort to manual EVM bytecode analysis, security researchers have two options: Either they (1) decompile the contract’s EVM bytecode to analyze the resulting pseudo source code or they (2) disassemble the contract’s EVM bytecode to analyze the human readable opcode instructions as shown in Listing3.

There are only a few available for EVM bytecode, some of which are only available for the professional tools. All of the following tools include an EVM . Free available : (1) The Online Solidity Decompiler55 is a browser tool, which offers a decompiler, disassembler, and an extensive overview on EVM opcodes. EVM runtime56 bytecode can be provided by either pasting the code directly or by loading it from its address on the respective Ethereum network. (2) Porosity [45] is a tool originally developed by Matt Suiche, and now unmaintained57. Professional tools that support EVM decompiler modules: (1) The JEB Decompiler supports an Ethereum smart contract decompiler58. (2) EthRays is a plugin for the professional reverse engineering tool

55Online decompiler at ethervm.io/decompile. (Last visited March 21, 2019.) 56The EVM bytecode without the loader code. The decompiler outputs a warning. 57The tool is still available at github.com/comaeio/porosity. (Last visited March 21, 2019. 58Cfr. www.pnfsoftware.com/blog/ethereum-smart-contract-decompile r/. The author is Nicolas Falliere. (Last visited March 21, 2019.)

55 Binary Ninja59 developed by Ret2 Systems60, which offers a disassembler and decompiler for EVM bytecode.

As disassembling EVM Bytecode is easier, there are more tools available. Apart from the tools already mentioned, these are available EVM bytecode disassem- bler: (1) The disassembler on Etherscan.io. All contracts are available as EVM bytecode and EVM assembly code on all Ethereum networks: (a) Mainnet (ID 1), (b) Ropsten (ID 3), (c) Rinkeby (ID 4), and (d) Kovan (ID 42)61. (2) Etherplay62 is another plugin for . (3) IDA-EVM63 is a plugin for the professional reverse engineering tool IDA64. (4) evmdis65 is implemented in Go. (5)ethdasm66. is implemented in Python. (6) The Remix IDE67 offers a disassembler and debugger, which was used to analyze the EVM opcode in Listing 3. Another powerful reverse engineering tool is (7) radare268, a console tool, which offers functionalities that rival those of professional tools like Binary Ninja and IDA69. Fedor Sakharov wrote a plugin for : evm.

EVM bytecode analysis requires an understanding of how the EVM works at bytecode level, and how transactions sent to smart contracts affect the execution of their EVM bytecode. The first part of the present work intended to provide in-depth practical and theoretical, albeit selective, background knowledge, which is essential for reverse engineering smart contracts, and eventually finding successful exploits. Anyone trying to analyze EVM bytecode manually for the first time will notice that it is a time-consuming task, which takes time and practice. For this reason, many

59Binary Ninja is available at binary.ninja. (Last visited March 21, 2019.) 60Cfr. blog.ret2.io/2018/05/16/practical-eth-decompilation/. (Last vis- ited March 21, 2019.) 61Etherscanners and disassembler for the test networks are available at .etherscan.io respectively. 62Etherplay is available at github.com/crytic/ethersplay. (Last visited March 21, 2019.) 63IDA-EVM is available at github.com/crytic/ida-evm. (Last visited March 21, 2019.) 64IDA is available at www.hex-rays.com/products/ida/. (Last visited March 21, 2019.) 65evmdis is available at github.com/Arachnid/evmdis. (Last visited March 21, 2019.) 66ethdasm is available at github.com/meyer9/ethdasm. (Last visited March 21, 2019.) 67The Remix IDE is available at remix.ethereum.org. (Last visited March 21, 2019.) 68radare2 is available at rada.re/r/down.html. (Last visited March 21, 2019.) 69Cfr. www.radare.org/r/cmp.html. (Last visted March 21, 2019.

56 security researchers have developed tools, which automate EVM bytecode analysis and present a valuable addition to a smart contract security analysis toolchain.

2.4 Requirement 3: Automated Analysis (Theory)

Smart contract developers and security analysts alike are interested in whether smart contracts as programs behave as expected. Testing the correctness of programs has always been a major concern of programmers. It follows an introduction to symbolic and concolic execution, two techniques that are used by the existing automated EVM bytecode analysis tools. The concepts explained in this section are intended to increase the awareness of how automated program analysis tools can facilitate and improve smart contract security analysis.

2.4.1 Symbolic Execution

According to James C. King [30], until the mid-seventies, two methods dominated the testing technology: (1) program testing, which supplies a program with a sample of data to see whether the program handles the data as expected, and (2) program proving, which tries to show that a program meets its specifications for all executions by a formal proof. Program testing utilizes unit tests, whose result depends on the chosen sample of data: For any data not included in the sample, the behavior of the tested programs remains unknown. Program proving, on the other hand, depends on the specification of the program and the subsequent development of the proof.

King [30] presented an enhanced testing technique, which created a middle way between the former extremes, and had a significant impact on program testing: symbolic execution. Unlike with unit tests, during symbolic execution, a program is neither actually executed nor is the program given sample inputs. Instead, the program is executed symbolically, creating a control flow graph using symbolic values. The symbolic values are referred to as classes of inputs because the result

57 of one symbolic execution of a program corresponds to the combined result of many different unit tests with their respective sample inputs, i.e., a symbolic value is equivalent to a class of particular inputs in unit testing. Back in the 1970s, King and his collaborators built a system, EFFIGY [30], which used symbolic execution for program testing and debugging. Thirty years later, advances in constraint satisfiability have made symbolic execution the essential technique of modern program analysis tools [9].

Symbolic execution is best understood by looking at an example. The code in Listing9 shows the simple function foo, which takes two arguments, x and y, of type integer. The function contains two control structures, i.e., IF statements, whose results depend on the given arguments x and y. Depending on the values of these input parameters, the control structures impact the function’s local variables a and b. At the end of foo, the local variables are tested by the assert function.

1 void foo(int x, int y){ 2 int a = 0,b = 2; 3 if (x != 0) { 4 a=4+b; 5 if (y == 1) { 6 b=2 * (x-y); 7 } 8 } 9 assert(a-b != 0); 10 } Listing 9: Code example for symbolic execution. Figure 18 shows the corresponding control flow graph (CFG).

Control flow graph (CFG)

During symbolic execution, the symbolic engine constructs a control flow graph (CFG) of the program, e.g., the function foo. The objective of the CFG is to outline all available paths the program may follow during execution. For the construction

58 of the CFG, the symbolic execution engine uses an interpreter, which reads the next statement of the program, e.g., of the function foo. The interpreter evaluates the expressions included in the statement. For each variable, whose value cannot be evaluated to a concrete value through static analysis, the engine uses symbolic values. The symbolic engine then maps the respective variables to these symbolic values, e.g, x 7→ sx, and stores this information in its symbolic store σs, e.g.,

σs = {x 7→ sx, y 7→ sy} as seen in Figure 18. Variables are associated either with expressions over concrete values, e.g., σs = {a 7→ 6}, or symbolic values, e.g.,

σs = {b 7→ 2(sx − sy)}. Control structures cause the control flow to split, creating different paths in a program. Each of the resulting paths can only be followed if the program variables meet the precise constraints imposed by the control structures, which created the paths in the first place. The symbolic engine concatenates these constraints in a propositional formula for each path and assigns them to the path constraint π, e.g., π = [x = 0 ∧ y 6= 1]. Initially, the path constraint π is set to true: π = [true]. If the path constraint at the end of a path evaluates to true as well, then the program state represented by that path constraint, e.g., an error state, is reachable.

Execution states constitute the CFG

For each statement the interpreter evaluates, the symbolic engine records an ex- ecution state. An execution state contains (1) the state of the engine’s symbolic store σs, (2) the path constraint π, as well as (3) the next statement nextStmt. The interpreter evaluates a statement and reads the next statement. The symbolic engine stores the results in the execution state of the evaluated statement. Then, the inter- preter evaluates the next statement, and the process repeats. The statements used in the code example from Listing9 include assignments and conditional branches.

Evaluation of these statements affects the symbolic store σs and the path constraint π. Assignments only change the symbolic store, whereas a conditional branch affects both, the symbolic store and the path constraint. The symbolic execution process forks and creates two execution states, i.e., the control flow is split into two paths. The symbolic engine follows these paths separately. Jumps advance

59 the symbolic engine to the statement at the respective jump destination70. All execution states collectively constitute the CFG71, which is a directed graph. The execution states represent the vertices, while the edges represent the constraints. The constraints need to be true to reach the respective next execution state dur- ing concrete execution. Figure 18 shows a CFG composed of all the execution states the symbolic engine discovers when symbolically executing the code from Listing9.

70On the bytecode level, the jump destination of a direct jump is encoded in the bytecode instructions. A problem arises if there is an indirect jump, whose jump destination is not encoded in the bytecode, but a value resulting from execution. This poses a major challenge [41] to the correct generation of the CFG. Indirect jumps can be (1) computed, e.g., from a jump table, (2) context-sensitive, e.g., be dependent on the arguments passed to a function, or (3) object-sensitive, which is a special case of context-sensitive that refers to object polymorphism creating virtual functions depending on the object type of the arguments. 71A CFG is called sound if all possible execution states have been found. Undetected indirect jumps can have a severe impact on the soundness of the CFG. Soundness is the true positive rate of indirect jump destination identification [41]. A CFG is called complete if the included execution states can actually be reached. Completeness is the inverse of the false positive rate of indirect jump destination identification [41].

60 (A) σs = {x 7→ sx, y 7→ sy} π = [true] nextStmt: a = 0; b = 2; line 2

(B) σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [true] nextStmt: if (x != 0); line 3

sx 6= 0 sx = 0

(C) (G) σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [sx 6= 0] π = [sx = 0] nextStmt: a = 4 + b; line 4 nextStmt: assert (a - b != 0); line 9

(D) (Result G) σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} 0 − 2 = 0 ∧ sx = 0 ⇐⇒ false π = [sx 6= 0] =⇒ No error nextStmt: if (y == 1); line 5

sy = 1 sy 6= 1

(E) (H) σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0 ∧ sy = 1] π = [sx 6= 0 ∧ sy 6= 1] nextStmt: b = 2 (x - y); line 6 nextStmt: assert (a - b != 0); line 9

(F) (Result H) σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2(sx − sy)} 6 − 2 = 0 ∧ sx 6= 0 ∧ sy 6= 1 ⇐⇒ false π = [sx 6= 0 ∧ sy = 1] =⇒ No error nextStmt: assert (a - b != 0); line 9

(Result F) 6 − 2sx + 2sy = 0 ∧ sx 6= 0 ∧ sy = 1 true if sx = 4 ∧ sy = 1 =⇒ Error

Figure 18: The control flow graph (CFG) the symbolic engine creates for the code from Listing9.

61 Constraint solver

Results F, G, and H in the CFG from Figure 18 show that the final path constraint in the leaves (F), (G), and (H) contains the condition for the case, in which the assert function throws an error. Results G and H will always evaluate to false because the variables a and b on those paths are both associated with concrete values, which do not fulfill the constraint. In Result F, however, b is associated with symbolic values, e.g., b 7→ 2(sx ∧ sy), and there exists a valid solution, for which the path constraint is true, e.g., if sx = 4 ∧ sy = 1.

Symbolic execution does not compute this solution. To reach conclusions such as the above, i.e., to find a solution to the given path constraint π, symbolic engines use a constraint solver. Path constraint problems fall into the category of satisfiability modulo theories (SMT) problems, i.e., a decision problem posed through a propositional formula such as 6 − 2sx + 2sy = 0 ∧ sx 6= 0 ∧ sy = 1 in Result F. The constraint solvers that have been developed for such problems, e.g., Z3 [14], are called SMT solvers.

Constraint solver finds feasible paths

Additionally, Symbolic engines use constraint solvers to decide, whether a path is feasible or infeasible. An infeasible path is one that cannot be followed because the constraint for entering such a path never evaluates to true. This evaluation is deduced from the path constraint the symbolic engine has formed up until the path’s entry point. If, for instance, the constraint for the new path to be followed is sy − sx > 0, but the path constraint of the path followed so far contains the constraint sx > sy, then the new path cannot be followed because sx > sy ∧ sy − sx > 0 ⇐⇒ false: the new path is infeasible. Infeasible paths thus represent program code, which cannot be reached. A constraint solver only produces inputs for feasible execution paths. Thus, the program can be represented as an execution tree consisting only of feasible execution paths. At the end of every feasible execution path, the constraint solver computes a solution for the path constraint contained in the respective execution state.

62 Programs are not as self-contained as the inspected function foo above. Real- world programs interact with their environment. They call functions from libraries or make system calls. These interactions can produce outcomes, to which the symbolic engine’s interpreter is oblivious, and for which the interpreter generates an unsolvable constraint. As a technique, symbolic execution only yields inputs, if the interpreter’s evaluation of statements generates a path constraint that can be solved by the constraint solver. Hence, simple function calls can cause symbolic execution to fail, and it does not produce any inputs, even though they exist.

Example of symbolic execution failure

Consider the two versions of function roon in Listing 10, and the function test in Listing 11. It is assumed (1) that the interpreter cannot evaluate the outcome of version one because the symbolic engine is oblivious to the code inside function roon, and (2) that the constraint solver cannot solve non-linear arithmetic problems, which is the case in version two of function roon.

1 # Version one of function roon: 2 int roon(int x){ 3 return 3 * x; 4 } 5 6 # Version two of function roon: 7 int roon(int x){ 8 return x * x; 9 } Listing 10: Two versions of function roon.

63 1 void test(int x, int y){ 2 int a; 3 a= roon(x); 4 if (y ==a){ 5 ERROR; 6 } 7 } Listing 11: Function test using function roon.

If the function test is being executed symbolically, the interpreter will evaluate the statement if(y == a) in line 6 from Listing 11, and generate the following constraints depending on which version of function roon is used: (1) version one produces the constraints sy = roon(sx) for the if-path and sy 6= roon(sx) for the else-path, and (2) version two sy = sxsx for the if-path and y 6= sxsx for the else-path, none of which can be solved by the assumed constraint solver. Thus, symbolic execution cannot produce any input values for the parameters x and y of function test. In the case of an existing error in the program, the result generated by symbolic execution is a false negative. The possibility of not producing any input values if the path constraint of a feasible path is unsolvable, and thereby creating false negatives, is the main disadvantage72 of symbolic execution. A solution to this problem is to use concrete inputs when needed to handle code that either cannot be traced by the symbolic engine’s interpreter or which generates an unsolvable path constraint.

Combining concrete execution with symbolic execution

Concolic execution is combination of concrete and symbolic execution. While symbolic execution only keeps track of its symbolic store σs, concolic execution has two stores: (1) a symbolic store σs, and (2) a concrete store σc. There are different approaches to combining concrete and symbolic execution. These approaches

72Other disadvantages [5] result from the following occurrence in real-world programs: (1) memory and the manipulation of pointers, arrays and complex data structure therein; (2) path explosions caused by loops: time constraint impede the exploration of all possible execution states; (3) constraint solving in general and non-linear arithmetic problems in general.

64 can be distinguished according to the way concrete and symbolic execution are interlaced.

Execution-generated testing (EGT) [9]: EGT uses concrete execution only if all needed values are mapped to concrete values. Tools such as KLEE [8] check after every execution state whether all values in the next statement are stored as concrete values. If that is the case, the next statement is executed concretely. If some of the values stored are symbolic, the program is executed symbolically. This strategy is applied to assignments as well as branch conditions.

Selective symbolic execution(S2E)[5]: In S2E, exploration is informed by the degree of importance programmers give to specific parts of the code at the time of the test. In the case of function test calling version two of function roon, there are two scenarios: (1) function test is executed concretely or (2) symbolically when roon is called. In the first scenario, S2E switches from concrete to symbolic execution, using symbolic arguments to call roon, and executes roon symbolically. Additionally, S2E executes roon concretely, and returns concrete values to the calling function test, where S2E resumes concrete execution. In the second scenario, S2E switches from symbolic to concrete execution. Concrete arguments are used to call roon, and roon is executed concretely. When S2E returns, symbolic execution is resumed in function test. This way, if the function roon cannot be explored symbolically as seen above, S2E has the possibility to explore roon concretely.

Dynamic symbolic execution (DSE) [5] or dynamic test generation is a popular form of concolic execution, where concrete execution directs symbolic execution:

The values of the σc are initialized with random input values. Concolic execution analyzes a program both with concrete and symbolic execution, and updates both stores. Concrete execution takes a path based on the values stored in σc, and symbolic execution follows the same path. Any constraints extracted from that path are added to the path constraint π. The respective branch conditions are tested immediately by concrete execution, which follows one or the other path depending on the values in σc. Symbolic execution follows concrete execution. Therefore, symbolic execution does not need the constraint solver to decide, whether a path

65 is feasible. To explore a different path, any of the constraints in π can be negated, going back to the execution state before the one, where the constraint was formed. The constraint solver computes the necessary input values to fulfill the negated

constraint, updates σc accordingly, and allows concrete execution to follow the other path. This process can be repeated as often as is necessary to reach the highest percentage of code coverage possible. It follows an example of DSE, which shows the difference between symbolic and concolic execution more clearly.

Example of DSE

Using the code example from Listing9 and the corresponding CFG from Figure

19. In execution state A, the concolic engine’s concrete store σc is initialized

as follows: σc = {x 7→ 1, y 7→ 1}. The symbolic store is initialized as usual:

σs = {x 7→ sx, y 7→ sy}.

1. (A) σc = {x 7→ 1, y 7→ 1} σs = {x 7→ sx, y 7→ sy} π = [true] 2. (B) σc = {x 7→ 1, y 7→ 1, a 7→ 0, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [true] 3. (C) σc = {x 7→ 1, y 7→ 1, a 7→ 0, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [sx 6= 0] 4. (D) σc = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0] 5. (E) σc = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0 ∧ sy = 1] 6. (F) σc = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0 ∧ sy = 1] 7. At the end of the path, after evaluating Result F, the concolic engine negates the last path constraint it received: ¬(sy = 1). 8. The constraint solver computes an input for ¬(sy = 1), e.g., y 7→ 2, and sets the concrete store accordingly: σc = {x 7→ 1, y 7→ 2, a 7→ 6, b 7→ 2}. The concolic engine goes back to the execution state before the one, where the path constraint sy = 1 was formed: execution state D.

66 9. Informed by the new input for y at execution state D, the concolic engine follows the path that fulfills the constraint sy 6= 1, and gets to execution state H.

10. (H) σc = {x 7→ 1, y 7→ 2, a 7→ 6, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0 ∧ sy 6= 1]

Provided with the initial concrete store σc, the concolic engine first lets symbolic execution explore the path. The concrete values of σc lead to execution state F: A B C D E F . After solving for concrete inputs for the negated form of the last received path constraint, the concolic engine lets symbolic execution explore another path. Starting this time from execution state D, the new concrete values lead to execution state H: A B C D H.

67 (A) σc = {x 7→ 1, y 7→ 1} 1. σs = {x 7→ sx, y 7→ sy} π = [true] nextStmt: a = 0; b = 2; line 2

(B) σc = {x 7→ 1, y 7→ 1, a 7→ 0, b 7→ 2} 2. σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [true] nextStmt: if (x != 0); line 3

sx 6= 0 sx = 0 (C) (G) σc = {x 7→ 1, y 7→ 1, a 7→ 0, b 7→ 2} σc = {x 7→ 0, y 7→ 2} 3. σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} σs = {x 7→ sx, y 7→ sy, a 7→ 0, b 7→ 2} π = [sx 6= 0] π = [sx = 0] nextStmt: a = 4 + b; line 4 nextStmt: assert (a - b != 0); line 9

(D) (Result G) σ = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} 4. 4. c 0 − 2 = 0 ∧ sx = 0 ⇐⇒ false σc = {x 7→ 1, y 7→ 2, a 7→ 6, b 7→ 2} 8. =⇒ No error σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} 8. π = [sx 6= 0] nextStmt: if (y == 1); line 5

sy = 1 sy 6= 1 (E) (H) σc = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} σc = {x 7→ 1, y 7→ 2, a 7→ 6, b 7→ 2} 5. σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} 10. σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2} π = [sx 6= 0 ∧ sy = 1] π = [sx 6= 0 ∧ sy 6= 1] nextStmt: b = 2 (x - y); line 6 nextStmt: assert (a - b != 0); line 9

(F) (Result H) σc = {x 7→ 1, y 7→ 1, a 7→ 6, b 7→ 2} 6 − 2 = 0 ∧ sx 6= 0 ∧ sy 6= 1 ⇐⇒ false 6. σs = {x 7→ sx, y 7→ sy, a 7→ 6, b 7→ 2(sx −sy)} =⇒ No error π = [sx 6= 0 ∧ sy = 1] nextStmt: assert (a - b != 0); line 9

(Result F) 6 − 2sx + 2sy = 0 ∧ sx 6= 0 ∧ sy = 1 true if sx = 4 ∧ sy = 1 =⇒ Error

Figure 19: The control flow graph (CFG) the concolic engine (DSE) creates for the code from Listing9.

68 Heuristic search methods

Heuristic search methods [5] describe the strategies symbolic engines use for path selection. For DSE to reach the highest percentage of code coverage possible, the concolic engine must decide, which path to explore. This decision process requires efficient search heuristics [5]. The above example of DSE is based on the directed automated random testing technique, DART [28]. DART uses a depth-first search heuristic. Hence, the last constraint encountered in a path is negated first. The breadth-first search would be the opposite strategy. KLEE uses random path selection, assigning probabilities to the paths depending on the length of a path, and how often a path has been explored. The heuristic methods of tools such as KLEE and S2E are both aimed at maximum code coverage. Other tools like the AEG system [4] prioritize paths, which lead to paths of greater interest according to the objective of the test. Thus, the use of a specific symbolic engine depends on the objective the test pursues. In smart contract security analysis, the objective is to identify vulnerable functions and develop an exploit consisting of inputs that trigger the vulnerable code segments of a contract.

Fuzzing

Fuzzing [41] refers to a form of dynamic concrete execution. Instead of executing a program on test cases, fuzzing executes the program on malformed inputs, which are modified during execution depending on the generated findings. However, these malformed inputs need to be adapted to the program in order to explore the program’s functionality in depth: test cases have to be formulated.

Symbolic-assisted fuzzing and semantics

Symbolic-assisted fuzzing [41] uses a symbolic engine, which modifies the fuzzer’s inputs according to the symbolic engine’s semantic insight into the program. In the case of symbolic-assisted fuzzing, the path constraint formed by the symbolic en- gine provides the semantic insight the fuzzer lacks. The semantic insight increases with concolic engines, which use heuristic search methods that enable them to direct the fuzzer to interesting paths.

69 Symbolic execution vs. Fuzzing

While research is in favor of symbolic execution analysis, in the security industry, symbolic engines are regarded as impractical [41]. Fuzzers seem to have a higher success rate of finding vulnerabilities [41]. Closer analysis of the results [41], however, shows that the higher failure rates of symbolic engines can be attributed to (1) program complexity and (2) the resulting length of paths thereof, both of which are exponentially proportional to each other.

Compared to programs running on common operating systems, the Ethereum virtual machine is simple73, and for typical smart contracts, the CFG can be generated with one hundred percent path coverage [34].

2.5 Requirement 4: Automated Exploit Development

Several tools and have been developed over the past couple of years, which facilitate automated analysis of Ethereum smart contracts: (1) Manticore [36], (2) OYENTE [32], (3) Mythril [34], (4) Rattle [38], Etherplay, (5) Pakala [3], (6) Octopus [48], and (7) TEETHER [31] All of them use symbolic execution as their underlying technique to create a CFG of a smart contract’s EVM opcode or an intermediate language, and solve the path constraint with an SMT solver, e.g., Z3 [14]. However, the way these path constraints are created depends on the objectives the tool designers have implemented regarding known vulnerabilities. In other words, the generation of path constraints depends on the definition of what a successful exploit is. The present work continues to follow the definition laid down by Krupp and Rossow [31], the developers of the tool TEETHER74.

Exploit as a solution to a path constraint

73The simplicity of the EVM allows for less interaction between the program and the environment. Hence, the possibility for the symbolic engine to encounter feasible paths it cannot explore is reduced by EVM’S design. 74At the USENIX Security conference 2018 and in their paper [31], Krupp and Rossow announced they would give contract owners from the Ethereum community 180 days before they wanted to publish their tool TEETHER. The tool has not been published, yet, at the time of writing.

70 A successful exploit [31] was defined in section 1.4.3 as a – possibly empty – sequence of state-changing transactions to a vulnerable contract, followed by a critical transaction. In the context of automated code analysis, the payload of both, state-changing and critical transactions, can be defined as the solution to a path constraint. The path constraint is formed during the generation of the CFG.

Path constraints and the semantics of syntax

Even in a simple program such as the function foo in Listing9, finding input values for which the function foo produces an error does not only depend on the symbolic engine’s use of a constraint solver. The actual constraint represented by the syntax of the assert function, i.e., 6 − 2sx + 2sy 6= 0, yields infinitely many solutions. The constraint solver, however, solves a much easier, more suitable path constraint, which best represents the semantics of the assert function. Hence, the symbolic engine needs to know in advance, which constraint is more suitable when the interpreter encounters a specific statement, e.g., the interpreter must know how to interpret the assert function.

Moreover, the example of the assert function assumes that the interpreter deals with a function signature, when, in fact, it does not. The C-like source code used in the examples above served the purpose of visualizing the inner workings of symbolic and concolic engines. The symbolic engines used in EVM bytecode analysis execute either EVM assembly code or their own intermediate language75. An assert function76 in EVM opcode is only represented by its four-byte hexadecimal function selector. Any semantic insight into the EVM opcode syntax must be modelled externally by the engine’s designer.

Path constraints suited for successful smart contract exploit generation

The CFG that is generated from the smart contract’s EVM opcode must be scanned

75For instance, the symbolic execution tool OYENTE [32] uses ETHERLITE, a language similar to EVM opcode. 76In Solidity, the require would be used. Cfr. medium.com/blockchannel/the-u se-of-revert-assert-and-require-in-solidity-and-the-new-revert-o pcode-in-the-evm-1a3a7990e06e. (Last visited March 21, 2019.)

71 for suitable instructions, which serve the overall object [34] to develop a successful exploit. Following the critical instructions identified earlier allows to narrow the search [31]: The engine is instructed to only analyze for SELFDESTRUCT, CALL, and DELEGATECALL. In addition, the interesting paths are searched for instructions, which allow for the control of the stack argument to. Krupp and Rossow [31] identified different EVM instructions that allow for that control: ORIGIN [25] retrieves the sender of the transaction, while the CALLER [25] instruction retrieves the sender of the initial message call. Both instructions would retrieve the attackers’ address, as no further message call has been sent at this point. The address could also be retrieved from the input data with the respective instructions, e.g., CALLDATALOAD, or from memory with MLOAD [25]. Prior successful state-changing transactions would also allow the attackers to use the SLOAD [25] instruction, which retrieves data from storage.

Additonal environment model constraints

The most difficult part of designing a symbolic execution engine are interactions with the environment. For Ethereum smart contracts analysis, the following en- vironment behaviors have to be modelled to reflect correct EVM environment behavior during constraint generation [31]: (1) SHA3 hash operations for input data from the message call. (2) Copying byte arrays of arbitrary length (a) from the input data of the message call or (b) from memory. (4) The concrete variables of a contract account’s state, e.g., its balance and storage content. In the first three cases, the environment can be modelled by mapping variables to symbolic variables, which in turn are mapped to concrete data [31]. However, a contract’s account state cannot be modelled. The concrete values of the storage content and the balance is necessary for constraint generation. Mythril, for instance, does not know the account state; all values are set to zero [34]. Krupp and Rossow [31] noticed a difference in behavior when testing exploits, which were created in an uninformed environment, on contracts with a concrete account state.

Time constraints

Another issue with symbolic execution is the time constraint. Contracts that call

72 other contracts, e.g., libraries, cause path explosions [34]. Moreover, in the case of necessary state-changing transactions, the inputs of the state-changing transaction will generate a new CFG that must be analyzed. Thus, the possible number of state- changing transactions has to be limited. Possible unsolvable constraints cannot be detected, but have to be mitigated with a time limit [31].

2.6 Toolchain for Automated EVM Bytecode Analysis and Ex- ploit Development

For minimal toolchain that fulfills the requirements established above, the present work recommends the installation and use of the following tools:

(1) solc

(2) py-solc

(3) Mythril

(4) Metamask

(5) Remix IDE

Frameworks like Truffle can only use the installed version of the Solidity compiler, only deploy contracts of the same version. The Remix IDE allows free choice of version. Ganache generates a local blockchain environment similar to the one, the JavaScript VM in Remix generates. However, Ganache experiences problems with false out-of-gas exceptions. The option of relying on a standalone setup with only the Web3 API for deployment was not considered by the present work.

Metamask allows for a user-friendly account and network management. Access to the Ethereum networks is provided via INFURA77. The main advantage is the option to send raw data in a transaction, which is more efficient than using command line clients like geth and their Web3 interface; especially for beginners.

77INFURA is a Web3 provider that allows access to the Ethereum networks

73 Remix allows compiling a minimal contract source code with undefined functions, i.e., pseudo source code, whose function names match the function signatures of an already deployed contract. The At Address functionality allows users to associate the function interface with the already deployed code. This way, security analysts can interact with contracts, for which they only have the EVM bytecode. In addition, Remix offers a debugger that can go through successfully executed transactions, showing the values on the stack, in memory, and in storage.78

The symbolic execution analysis tool Mythril can either be used as a standalone command line tool or be integrated with the Remix IDE, which gives Remix another advantage.

The minimal toolchain is very efficient as no programming is necessary. Beginners can concentrate their efforts on the analysis alone, and advanced users can integrate reverse engineering tools into their toolchain.

Conclusions and Future Work

The present work has introduced the reader to the Ethereum blockchain. First, on a conceptual level, explaining general blockchain concepts, and then looking at the Ethereum blockchain in particular from different perspectives. Second, on a practical level, the main components that make up the Ethereum blockchain have been explained in detail. In preparation for the objective of the present work, which was the analysis of EVM bytecode from an attacker’s perspective, smart contracts have been introduced. Both, on the level of EVM bytecode and Solidity source code. In addition, critical assembly instructions relevant to the exploitation of smart contracts have been explained in detail. The reader was equipped with a definition of what constitutes a vulnerable contract and a successful exploit, both of which were later combined with the definition of successful automated bytecode analysis. In addition, the present work introduced requirements for a possible smart

78For more advanced analyses radare2 is recommended.

74 contract analysis toolchain. These requirements have been viewed individually, while theoretical focus was put on automated bytecode analysis and symbolic execution as this is the underlying technique of automated smart contract analysis tools. The importance of semantics was highlighted with respect to designing automated tools for smart contract exploitation. At the end, a minimal toolchain is presented, which allows beginners to efficiently analyze smart contracts and develop exploits.

Future work on a toolchain for smart contract bytecode analysis should integrate more fully the possibility of deploying EVM bytecode directly to the network. The security awareness of the Remix IDE is proven by the fact that security tools like Mythril are integrated. Integration of direct EVM bytecode deployment is a necessity for any efficient security analysis. With respect to automated bytecode analysis, an additional requirement has risen: The necessity to copy an account’s state, e.g., its storage. The storage constitutes the real environment of a contract. Symbolic execution needs the input of the contract’s current state to produce successful exploits. Being able to copy or download the state, and integrate it into automated tools like Mythril would be an excellent addition to the smart contract security analysis toolchain.

75 References

[1] Anon. Script. en.bitcoin.it/wiki/Script. (Last visited March 12, 2019.).

[2] Bisola Asolo. Orphan, uncle & genesis blocks explained. www.mycryp topedia.com/orphan-uncle-genesis-blocks-explained/. (Last visited February 24, 2019.).

[3] Korantin Auguste. Pakala: Yet another evm symbolic execution tool. www.palkeo.com/en/projets/ethereum/pakala.html. (Last visited March 17, 2019.).

[4] Thanassis Avgerinos, Sang Kil Cha, Brent Lim Tze Hao, and David Brumley. Aeg: Automatic exploit generation. In Network and Distributed System Security Symposium, February 2011.

[5] Roberto Baldoni, Emilio Coppa, Daniele Cono D’Elia, Camil Demetrescu, and Irene Finocchi. A survey of symbolic execution techniques. ACM Comput. Surv., 51(3), 2018.

[6] Daniel R. Brown. Elliptic curve cryptography. www.secg.org/sec1-v 2.pdf. (Last visited March 10, 2019.).

[7] Vitalik Buterin. A next-generation smart contract and decentralizated ap- plication platform. https://github.com/ethereum/wiki/wiki/ White-Paper. (Last visited March 14, 2019.).

[8] Cristian Cadar, Daniel Dunbar, and Dawson R. Engler. Klee: Unassisted and automatic generation of high-coverage tests for complex systems programs. In OSDI. USENIX Association, 2008.

[9] Cristian Cadar and Koushik Sen. Symbolic execution for software testing: Three decades later. Commun. ACM, 56(2), February 2013.

[10] Ronald H. Coase. The problem of social cost. Journal of Law and Economics, 3, 1960.

76 [11] Consensys. Truffle suite. truffleframework.com/docs/truffle/. (Last visited March 3, 2019.).

[12] R.D. Cooter. Allocation, Information and Markets, chapter The Coase Theo- rem. 1989.

[13] Micah Dameron. Beigepaper: An ethereum technical specifica- tion. github.com/chronaeon/beigepaper/blob/master/be igepaper.pdf. (Last visisted March 17, 2019.).

[14] Leonardo De Moura and Nikolaj Bjørner. Z3: An efficient smt solver. In Proceedings of the Theory and Practice of Software, 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems. Springer-Verlag, 2008.

[15] Ethereum. Dev’s p2p network protocol & framework. github.com/eth ereum/devp2p. (Last visited March 16, 2019.).

[16] Ethereum. Ethereum function signature database. www.4byte.direct ory. (Last visited March 17, 2019.).

[17] Ethereum. Ethereum homestead documentation. www.ethdocs.org/en /latest/index.html. (Last visited March 9, 2019.).

[18] Ethereum. Go ethereum. github.com/ethereum/go-ethereum/. (Last visisted March 16, 2019.).

[19] Ethereum. Javascript . github.com/ethereum/wiki/wiki/Ja vaScript-API. (last visited March 17, 2019.).

[20] Ethereum. Json rpc api. github.com/ethereum/wiki/wiki/JSON -RPC. (Last visited March 17, 2019.).

[21] Ethereum. Light ethereum subprotocol (les). github.com/zsfelfold i/go-ethereum/wiki/Light-Ethereum-Subprotocol-(LE S). (Last visited March 16, 2019.).

77 [22] Ethereum. Rlp. github.com/ethereum/wiki/wiki/RLP. (Last visited March 19, 2019.).

[23] Ethereum. Solidity. solidity.readthedocs.io/en/v0.5.5/. (Last visited March 8, 2019.).

[24] Ethereum. Swarm hash. github.com/ethereum/wiki/wiki/Swar m-Hash. (Last visited March 17, 2019.).

[25] Ethervm. Ethereum virtual machine opcodes. ethervm.io. (Last visited March 19, 2019.).

[26] Ethervm. Online solidity decompiler. ethervm.io/decompile. (Last visited March 19, 2019.).

[27] The Parity Wallet Hack Explained. Santiago palladino. blog.zeppelin. solutions/on-the-parity-wallet-multisig-hack-405a8 c12e8f7. (Last visited March 19, 2019.).

[28] Patrice Godefroid, Nils Klarlund, and Koushik Sen. DART: Directed Auto- mated Random Testing. In PLDI. ACM, 2005.

[29] Preethi Kasireddy. How does ethereum work, anyway? medium.com/@preethikasireddy/how-does-ethereum-wor k-anyway-22d1df506369. (Last visited March 12, 2019.).

[30] James C. King. Symbolic execution and program testing. Commun. ACM, 19(7), July 1976.

[31] Johannes Krupp and Christian Rossow. teEther: Gnawing at Ethereum to Automatically Exploit Smart Contracts. In 27th USENIX Security Symposium (USENIX Sec 18), August 2018.

[32] Loi Luu, Duc-Hiep Chu, Hrishi Olickel, Prateek Saxena, and Aquinas Hobor. Making smart contracts smarter. www.comp.nus.edu.sg/~loiluu/ papers/oyente.pdf, 2016. (Last visited March 21, 2019.).

78 [33] Morisander. The biggest smart contract hacks in history or how to endanger up to us $2.2 billion. medium.com/solidified/the-biggest-s mart-contract-hacks-in-history-or-how-to-endange r-up-to-us-2-2-billion-d5a72961d15d. (Last visited March 11, 2019.).

[34] Bernhard Mueller. Smashing ethereum smart contracts for fun and real profit. conference.hitb.org/hitbsecconf2018ams/mater ials/WHITEPAPERS/WHITEPAPER%20-%20Bernhard%20Muell er%20-%20Smashing%20Ethereum%20Smart%20Contracts% 20for%20Fun%20and%20ACTUAL%20Profit.pdf. (Last visited February 24, 2019.).

[35] Rachel O’Dwyer. Blockchain just isn’t as radical as you want it to be. longreads.com/2018/02/15/blockchain-just-isnt-a s-radical-as-you-want-it-to-be/. (Last visited March 11, 2019.).

[36] Trail of Bits. Magic with manticore. blog.trailofbits.com/2017/ 05/15/magic-with-manticore/. (Last visited March 17, 2019.).

[37] Trail of Bits. Not so smart contracts. github.com/crytic/not-so-s mart-contracts. (Last visited March 17, 2019.).

[38] Trail of Bits. Rattle. github.com/crytic/rattle. (Last visited March 19, 2019.).

[39] Bernard Peh. Solidity bytecode and opcode basics. medium.com/@bloc kchain101/solidity-bytecode-and-opcode-basics-672e 9b1a88c2. (Last visited March 18, 2019.).

[40] Thibaut Sardan. What is a light client and why you should care? www.pari ty.io/what-is-a-light-client/. (Last visited March 16, 2019.).

[41] Yan Shoshitaishvili, Ruoyu Wang, Christopher Salls, Nick Stephens, Mario Polino, Andrew Dutcher, John Grosen, Siji Feng, Christophe Hauser, Christo- pher Kruegel, and Giovanni Vigna. SOK: (state of) the art of war: Offensive

79 techniques in binary analysis. In 2016 IEEE Symposium on Security and Privacy (SP). IEEE, May 2016.

[42] SmartContractSecurity. Swc registry – smart contract weakness classification and test cases. smartcontractsecurity.github.io/SWC-reg istry/. (Last visited March 20, 2019.).

[43] Jimmy Song. The truth about smart contracts. medium.com/@jimmyson g/the-truth-about-smart-contracts-ae825271811f. (Last visited March 11, 2019.).

[44] ETH Gas Station. Eth gas station. ethgasstation.info/. (Last visited March 9, 2019.).

[45] Matt Suiche. Def con 25: Porosity – decompiling ethereum smart contracts. blog.comae.io/porosity-18790ee42827.

[46] Nick Szabo. Smart contracts. www.fon.hum.uva.nl/rob/Cours es/InformationInSpeech/CDROM/Literature/LOTwinters chool2006/szabo.best.vwh.net/smart.contracts.html, 1994. (Last visited March 11, 2019.).

[47] Andrew S. Tanenbaum and Maarten van Steen. Distributed Systems: Princi- ples and Paradigms (3rd Edition). Maarten van Steen, 2017.

[48] Patrick Ventuzelo. Octopus. github.com/quoscient/octopus. (Last visited March 19, 2019.).

[49] Patrick Ventuzelo. Reverse engineering of blockchain smart contracts. recon.cx/2018/montreal/schedule/system/event_att achments/attachments/000/000/053/original/RECON-M TL-2018-Reversing_blockchains_smart_contracts.pdf. (Last visited March 10, 2019.).

[50] A. F. Webster and S. E. Tavares. On the design of s-boxes. In Advances in Cryptology — CRYPTO ’85 Proceedings. Springer Berlin Heidelberg, 1986.

80 [51] Gavin Wood. Ethereum: A secure decentralised generalised transaction ledger. Ethereum project yellow paper, 2014.

81 Declaration of Authorship

I hereby declare that the thesis submitted is my own unaided work. All direct or indirect sources used are acknowledged as references. I am aware that the thesis in digital form can be examined for the use of unauthorized aid and in order to determine whether the thesis as a whole or parts incorporated in it may be deemed as plagiarism. For the comparison of my work with existing sources I agree that it shall be entered in a database where it shall also remain after examination, to enable comparison with future theses submitted. Further rights of reproduction and usage, however, are not granted here. This paper was not previously presented to another examination board and has not been published.

Ulm, March 22, 2019

82