Hey everyone! This is David Wong, and in this video I will talk about attacks on Ethereum smart contracts And I'm not making things up, I'm just giving you a tl;dr of this paper called "A survey of attacks on Ethereum smart contracts"

It's written by Nicola Atzei, Massimo Bartoletti and Tizianna Cimoli from the university of Cagliari (Italy) So if you want more details, be sure to check their paper Before I start showing off real world examples, actual contracts that got owned, I'm going to list some of the problems that can surprise developers of smart contracts The first one is named "Call to the unknown" Coding with Solidity allows you to call external contract's functions And you probably know the send() function which allows you to send money to someone's address

But what you might not know is that it also can lead to external code execution In Ethereum, an address can map to someone's wallet, but it can also be tied to a contract In this case, the fallback function of that contract will be executed as you're sending ether to the address That might surprise more than one developer Another function is call(), which allows you to invoke another function located in another contract or the current contract

For example here, I call the contract c's `ping()` function by its signature, which is the first 4 bytes of its hash declaration Notice here that I said "hash" instead of "SHA-3", this is because it's not really SHA-3, it's rather Keccak with an incorrect number of rounds, blablabla, that's a story for another day `n` here is the argument to the function ping, and you can see that I am also sending some ether as part of the call via the "amount" argument In the case where the function with the given signature does not exist (in the contract `c`) then the fallback function of `c` will be called instead (with no arguments) `delegatecall()` is a similar function to `call()` (with some subtle but dangerous differences, I'll talk about that later) and it has the same call-to-the-unknown problem

A usually better way to call a function is to have it declared by name as part of an interface or a contract, and call the name directly in your code But if we mistyped the declaration (in the interface or the contract), or if the address that you're calling end up not having such a function, the fallback (again) will be run instead There are several situations when an exception is raised in Solidity: if the execution runs out of gas (you get an out-of-gas exception), if the function manually throws (by using the throw function, which is now deprecated, so you can use the revert, require or assert function to throw an exception), if the call stack reaches its limit (so, by that I mean when you call a function, this function can call another function, and on and on, and every time you do this you have these nested calls and you cannot go further than a depth of 1024) This used to be a problem and I'll talk about that later, but it's not really a problem anymore because this has been fixed in more recent versions But anyway, let's look at an example, Bob here calls Alice's `ping()` function directly

And this `ping()` function throws, if it throws it will propagate (or "bubble-up" as they say) and any changes that took place in the execution will be reverted So here `x` will still be `0` in Bob's contract Now if Bob had called Alice's `ping` function via a `call()` (instead of calling it directly), the throw would not have propagated and the `EVM` (the Ethereum Virtual Machine) would have subsituted a `false` as the return value to the call to Alice's `ping` Therefore, the execution would have continued, setting `x` to `2` after finishing In general, all the low level functions will behave the same way

So beware of `call()`, `delegatecall()`, `callcode()` and `send()` (which is compiled the same way as a `call()` but with an empty function signature) Avoid these unless you can't, and prefer the function `transfer` which provide a similar behavior to the `send()` function, but it does propagate a `throw` if it fails So if you cannot avoid that, make sure that you check the return value of these functions for a false return value And if it returns false, for example you can throw again (via require, assert or revert) So in the event where you have a series of nested calls, a `throw` will propagate all the way up, up until a `call()` or `delegatecall` (or any of these low level functions) etc is reached And then it wil return a false and if it's not checked it won't propagate So if you don't have any of these, the execution will revert itself successfuly If you have any of these low level functions, be careful I said earlier that when you use the `send()` function to send some ethers to some address, the fallback function of "some address" (if it exists) will get executed

The amount of gas unit available to the callee is bound to 2300 units With 2300 units of gas, not so much can happen, certainly not a state changing execution, and if too many things need to be run the execution will throw with an out-of-gas exception In the example here, calling `pay` with the address of D1 will throw, `send()` will return `false` (as we talked about previously) and the ethers will not be paid calling it with the address of D2 will be fine This one is similar to the "call-to-the-unknown"

So, let's consider the following example: Here callers to Bob's `pong` function can provide an address as argument Hopefully the address of an `Alice` type of contract It doesn't have to be though! And like any best-practices guidelines, never trust user input If `c` is not a contract address, `pong` returns with no execution of code If `c` is the address of any contract (not only a contract of type `Alice`) that implements a `ping` function that takes a `uint` as argument, then the function will get executed Not matter how its behavior differs from the original `Alice` contract

If `c` is a contract that doesn't implement `ping()` (this function with the same signature), then the fallback function gets executed So Never trust user input Let's observe the following contract Anyone can run the `ping` function with an address as argument The `ping` function will send 2 ethers via a call with no limit on the unit of gas

And once the 2 ethers have been sent, the `sent` boolean is toggled and the function cannot be called again Now imagine that an adversary publishes the following malicious contract The adversary will provide the address of his malicious contract to Bob's `ping` function The `ping` function will send 2 ethers to the Mallory contract, and as we've seen previously this will trigger the execution of the fallback function inside of the malicious contract This function calls Bob's ping() function again, which will work since the former execution has not had time to finish and has not yet set the variable `sent` to `true` Thus, it will successfuly re-send 2 ethers to the same address and re-run the address' fallback function

This goes on and on until either the contract Bob goes out of money OR the execution runs out of gas OR the maximum depth of the call stack has been reached In all cases, an exception will be throwned, will cancel the last call, but will only propagate up until the last use of the `call()` function as we've seen in "exception disorder" This problem is called "re-entrancy" and has surprised everyone in the Ethereum community and has been quite an annoying problem for legitimate smart contracts This one is pretty straight forward, fields in contracts can be public or private But if you know how the blockchain works, you know that everything is REALLY public

The `private` type can be deceptive While you can't directly read the values out of the contract, you can look at the transactions and infer what the `private` values are from them Once a contract is in place, it's immutable Meaning that if a bug is found/if a vulnerability is found it is pretty much the end of that contract This is what happened to the DAO contract (and we'll talk about that later) where an attacker was able to withdraw ether after ether from the contract

The attack only reached a stop, when the Ethereum community decided to hard-fork the blockchain Not everyone agreed and this created two different blockchains But this is a story for a different video Since then, a lot of contracts include "upgradable" components, which goes against the spirit of "the contract is the law" but it prevents this kind of immutable vulnerabilities Most addresses in Ethereum are inexistent

Meaning no-one control their private keys and they have no contract associated to them Sending ether to these addresses, effectively deletes the ether from Ethereum Consequently, programmers have to manually ensure that the addresses they provide to transactions are always correct We've talked about it before, a function calling a function increments the call stack, and the call stack cannot go further than a depth of 1024 When this limit is reached, a further call will throw an exception

This vulnerability allows to induce a targeted `call()` to fail without throwing an exception, Because remember, it "bubbles-up", except that the call just returns false when the inside threw an exception When the return value is not properly checked, it can lead to exploits However, this has been fixed since october 2016 and any execution will always run out of gas before reaching a call stack of 1024 frames This is because the maximum amount of gas that you can use to call something prevents you to do this You need to think of a DAPP (distributed application) as an out-of-order protocol Like UDP (or not)

When calling a contract's function, you cannot predict in what state is the contract is (or will be) This is because miners can selectively re-order transactions inside of a block itself, or even choose not to process some transactions In some rare cases, a miner spotting your transaction, could even choose to craft one of his own before executing yours, in order to modify the state at the last moment Randomness is a hard problem for Ethereum, no value is really private and so it is somehow impossible to secretly seed a pseudo-random number generator On the other hand, while some values might appear kind of random and good candidates to seed pseudo-random number generators, they are not

Timestamps, nonces, block heights are all values that can be relatively influenced by the miners executing your transactions As stated previously, miners can choose timestamps with a certain degree of freedom And contracts should not rely "too much" on these values Now that we've seen this range of issues that have been found so far on Ethereum, let's see how they were exploited in practice, in order to steal some ethers The DAO was a contract implementing a crowd-funding platform, which raised around 150 millions of dollars before being attacked in 2016

An attacker managed to put around 60 millions of dollars under her control, until the hard-fork of the blockchain nullified the effects of the transactions involved in the attack There is more to the story but I will focus mostly on technical details in this video So let's take a look at a simplified version of their contract called SimpleDAO Notice that this code is not using the last version of Solidity, so the syntax is a bit old For example the donate address should have a `payable` keyword in order to be able to receive ether

More detailed contract made with more recent versions of Solidity are available at this address At this point you should pause the video to first read and understand the contract This simple contract allows participants to `donate` ethers to the contract, and to `withdraw` their ethers later Here imagine that an attacker publishes the following malicious contract, with 0x354 being the address of the published DAO contract Our attacker can first donate some ethers to Mallory's address via SimpleDAO's `donate` function After that, she can query the fallback function of Mallory which will attempt to withdraw the ether from the DAO's contract by querying its `withdraw()` function So here we first use the `queryCredit()` function to know what amount we can withdraw; then the first check of `withdraw` passes correctly since we're withdrawing the right amount

But before removing the amount being withdrawn from the account, we're sending the ether This looks like an OK thing to do until we remember about the "call to the unkown" problem When receiving the ether, Mallory's fallback function will get executed which will pretty much do the same call to the DAO's withdraw function which will work! since we still have the money on our account Remember, the contract still hasn't substracted the withdrawn amount What happens next is that we enter a loop that will continuously send money to the Mallory contract until either the execution runs out of gas, the DAO contract runs out of ether or the call stack's maximum depth of 1024 is reached

At this point the execution will throw an exception which will revert back any changes up to the last use of the `call` function which will return `false` This is what we talked about as the "exception disorder" problem if you remember Since the contract is not checking the return value of that call, it will not be able to throw Note that if the attack is limited by an out-of-gas exception, this can be delayed by sending more gas in the originating transaction A different attack allows an attacker to steal all the ether from the DAO contrat only using two calls to the fallback function

Let's take a look a the following malicious contract The adversary calls the attack function which donates a small amount of ether (1 wei) to the DAO contract then tries to withdraw it The same thing will happen again and the fallback of Mallory2's contract will be executed attempting to withdraw the same amount once more However, this time, the third execution of the attack is stopped by the `performAttack` bool which stops the execution correctly The credit of Mallory2 can finally be reduced of 1 wei, TWICE

This means that the malicious' contract credit on the DAO contract is now 2^256 – 1 wei because of the underflow To finalize the attack, the attacker can call the `getJackpot` function to withdraw the full amount of ether the contract possesses Our second attack is on "King of the Ether Throne", a game where players compete to become the king How do you become the king? You must pay some ether to the current king plus a small fee to the contract Every time the crown is moved the price to pay increases, making it less and less affordable to become the king

It's a great ponzi scheme that had a flaw, and I'll let you pause the video to appreciate their contract Whenever the player sends ether to the King-of-the-Ether contract, its fallback function is called The fallback first checks that the sent ether is enough to buy the title: if not, it throws an exception (reverting the ether transfer) If it's enough, the new compensation is calculated and sent to the current king; then the king is replaced with the sender; then a new price to uncrown the new king is calculated Fiou

See the problem here? And again, if you want to pause the video to think about it, kudos to you Here is what an attacker could do By publishing the following contract, and by using `unseatKing()` to get into the king's seat The adversary can successfuly run a Denial of Service on the targetted contract

Why? Imagine that the Mallory contract is now the king after having paid the required amount of ether, and that Bob comes along and decides to become the king himself When calling the fallback function of the King-of-the-Ether contract with the correct amount of ether, the fallback function will try to send ether to Mallory which will throw in the middle of the execution Because of how the `send()` function works, the EVM will replace that throw with a `false` return value which will be caught by the contract and an exception will be thrown again, reverting the effects of the execution Hence, Mallory is still the king and will probably remain the king forever Odds-And-Evens is a multiplayer game where two players choose a number and if the sum is even, the first player wins, if its odd, the second player wins An attacker here can wait for the first player to make his play wih the `play()` function which will be stored in the first index of the `players[]` array Eventhough the `players` array is set as `private`, which means that there is no getter to simply read the value from the contract, it is still readable by looking at the transaction

The attacker can then choose a winner number when playing his turn This is an instance of "keeping-secrets" where we realize that nothing is really secret on the blockchain Let's take a look at Rubixi, another Pyramid Scheme Ethereum is a great place to create Ponzi Schemes and Pyramid Schemes and other scams just because of the simplicity to deploy such schemes, the willingness to try these new cool DAPPs, the amount of ether owned by ethereum enthusiasts and smart contracts' grey area of legality Anyway, Rubixi was previously called "DynamicPyramid", but the name didn't really make it sound legit, so they changed the name to Rubixi

Except that they forgot to rename the constructor to Rubixi, letting anyone call it like a normal function This error is self explanatory so I'll let you guess what was wrong here, if you don't get it just ask in the comments The Governemental is another ethereum game where to play, a participant must send a certain amount of ether to the contract If no one joins a game for more than 12 hours, the last participant gets all the ether in the contract (except for a fee kept by the owner) A first version of the contract would use the following snippet to clear the list of players after a game ended

The problem is that the bytecode generated by this Solidity code would each location of the arrays one-by-one At some point the list of participants would grow so large that the amount of gas needed to complete the execution of this code was over the maximum allowed amount of gas for a transaction Now let's look at another approach that does not have this problem since it only keeps the last "investor" Once you've read this contract, let's look at an attack The first attack profits the owner of the contract

Once a player has won because of no one investing in the next 12 hours, the player can theoretically claim its jackpot with the `resetInvestment` function of the contract Unless! A malicious owner decides to call this function first and to make it fail somehow First the attacker will publish the following contract: The attacker can then call the only function of the contract with the address of the Governmental contract and a `count` of `0` The `attack()` function will call itself recursively 1023 times and finally call the `resetInvestment()` function of the Governmental contract This will be the execution's 1024th nested call, if the stack-size-limit vulnerability still worked (which it does not) any other call would either throw or return false

They would return false for low level functions like `send`, `call`, `delegatecall` and `callcode` What a coincidence, the `resetInvestement` itself has two such calls, one to send the jackpot to the last investor and one to send the rest to the owner Both these calls will fail and return `false`, which are not checked here Thus, the game will be reset and no ether will be distributed, leaving all of it for the contract OK

The second attack is more convoluted, but theoretically possible if the attacker were to be a miner with a bit of luck! Imagine that almost 12 hours have passed since the last investment, and right before the end, a transaction is made to invest, prolonging once more the Governmental game If the attacker is the one mining the block in which the transaction will be executed and recorded, he can simply place his own transaction and refuse to include others, or include others but re-order them so that his comes first (increasing the price and making the other transactions failing) This attack is leveraging the "unpredictable state" of the distributed blockchain The third attack also takes a miner, who will realize that there is one minute left for him to win! He's the last investor, and nobody else has invested Unfortunately, a lot of other players see the opportunity and start playing

In a moment of luck, or due to some computational advantage, the attacker mines a block! And thanks to the time-constraints vulnerability (which gives the miner some room to set the new block's timestamp), the newly mined block is set to a timestamp one minute in the past, invalidating any attempts to join the game The miner is the last man to stand and wins the jackpot Our last attack is a fictive one Imagine the following library that defines a set of useful functions on a map We can see the `storage` keyword used to pass the argument as a reference

Fields in libraries are immutable and are just here to serve as interface for the contracts making use of the library Now let's imagine that we might want at some point, to update the library without having to update ALL the contracts that are making use of the library What we could do, is to have an intermediate library that stores the library's address Now contracts that want to use the library can retrieve its address via this intermediate contract and the owner of the contract can update the library by pointing the address to the new library And we could imagine the following Bob contract, that would make use of the SetProvider that we've passed as argument during the contract deployement

To use the linked library, the contract first retrieves its address via the `getSet()` function of the SetProvider contract It then calls the address' functions directly This sounds like a good idea right? Now imagine if the SetProvider contract is compromised, or if the owner of the SetProvider contract decides to go rogue Whoever it is, this evil someone could publish the following fake library and make SetProvider point to it: Now, when Bob calls the `version()` function of the library, FOR EXAMPLE, what the Bob contract really does is that it executes a `delegatecall()` on the library's function What are the differences between a `call` and a `delegetecall` you may ask, well the delegatecall basically tells the function it calls "hey dude, do whatever you want with my storage, its yours"

That is pretty bad and pretty much why you should almost always avoid this function The meaning of `this` also changes to point to the caller's address and not the callee's This mean that when the malicious `version()` function is called, it will send ether from the caller (Bob) with the total amount of ether of this which is Bob's total amount of ether Worst, the malicious contract could have used the `selfdestruct()` function which annihilates the caller's contract from the blockchain and send its orphan ethers to the attacker's address That's it! If you want to know more about these attacks, check the paper

Again, it's called "A Survey of attacks on Ethereum smart contracts" And if you enjoyed this video, well I have plenty of others, other videos, I have a blog, follow me on twitter Be sure to check all that out, and if you have any questions use the comment section Cheers