Note: We will start off our Smart Contract Hacking journey with some basic solidity programming in the first two weeks. After that we will ramp things up and get a little crazy deploying blockchains and liquidating funds from accounts. But since the purpose of this series is to share the information I have learned over the last two years. I do not want to alienate those new to Smart Contracts and programming so we will take these first few weeks a bit slow.
Also note the text was taken from a book I was / am writing, I retrofitted it for this blog, and placed videos where screenshots may otherwise exist. If something seems off.. Just DM me on twitter and I will update it anything I might have missed during editing, but I tried to edit it as best as possible to meet this format rather then a book.
Cheers @Fiction
http://cclabs.io
Thanks to @GarrGhar for helping me edit/sanity check info for each of the chapters.
About Solidity
The solidity programming language is the language used to write smart contracts on the Ethereum blockchain. As of my initial writing of this chapter the current compiler version was 0.6.6. However, the versions change rapidly. For example, when I started coding in solidity 2 years ago, solidity was in version 4 and now its version 7 with major library and coding stylistic requirement updates in version 5.
So, note that when compiling your code for labs its best to use the version sited in that particular example. This is easily achieved in the online compilers, by selecting the compiler version from the dropdown menu. If you would like to give yourself a small challenge, use the latest compiler version and try to modify the code to work with it. Usually this just requires a few minor modifications and can be a good learning experience under the hood of how Solidity works and what has changed.
Solidity is very similar to writing JavaScript and is fully object oriented. In the intro chapters we will attempt to provide a quick overview of solidity understanding needed for a penetration tester. This will not be full guide to programming, as programming is considered to be a pre-requisite to application hacking. Instead this chapter will be a gentle introduction of needed concepts you will use throughout this book. Solidity is also a needed pre-requisite for understanding the rest of the information and its associated exploitation course.
However, as long as you understand general programming concepts then you should have no trouble understanding solidity. It is a relatively easy language to get up and running with quickly in comparison to more mature languages like C++ and Java which may take a more significant amount of time to learn.
The most important thing to understand with solidity is that unlike traditional languages, solidity handles transactions of monetary value by default. Meaning you don't need to attach to a payment API to add transactions to your applications. Payment functionality is baked into the language as its primary purpose and for usage with the Ethereum blockchain.All that's needed for financial transactions in solidity is a standard library transfer function, and you can easily send value to anyone's public address.
For example, the following simple function will transfer a specified amount of Ether to the user calling the function provided they have a large enough balance to allow the transfer. But lets not dive into that just yet.
Rather than discuss payments at this point, let's not jump to far ahead of ourselves. We need to understand the structure of a smart contract. Let's take a look at a Hello World example. We will analyze all of the key aspects that make solidity different then other languages you may currently understand.
You can easily follow along with this on http://remix.ethereum.org which is a free online IDE and compiler for coding in solidity. A full video walk through of Remix is included later on in this chapter. Remix contains in-browser compilers and virtual environments that emulate block creation and allow you to send and receive transactions.This is a powerful development tool and absolutely free to use.
Below is the simple code example we will analyze before moving on to a live walk through.
1.pragma solidity 0.6.6;
2.
3.contract HelloWorld {
4.
5.constructor () public payable {
6.//This is a comment
7.//You can put your configuration information here
8.}
9.
10.function hello () public pure returns (string memory) {
11.return"Hello World";
12.}
13.}
There is a lot going on in this small program so I will try to break it down as simple as possible. In the first line, we have the pragma statement which is required at the top of each program to let the compiler know which version of solidity this code was written for.As I said earlier, these versions change rapidly due to the evolving technology and many changes are implemented into each new version. So, the compiler needs to know which version you intended this to run on.
On line 3 is the word "contract" followed by whatever name you wish to call your contract. The contract's functionality is then enclosed in curly braces. This is similar to creating a class in any other language. It's a block of associated code that can be inherited, or interfaced with and contains its own variables and methods.
On line 5 contained within the contract curly braces we have a constructor denoted by the word "constructor".The constructor is run one time at contract creation and used to setup any variables or details of the smart contract. This is often used for creating an administrator of the contract or other items that are needed prior to contract usage.
Functions and variables within Solidity also have various types and visibility set with their creation.In this case also on line 5 you will see the words "public" and "payable" used to describe the constructor.
Public you may be familiar with as it's a common visibility keyword used in other languages denoting that anyone can call this function. There are other visibility types in Solidity listed below, we will cover each of these in more detail as we use them to our advantage when hacking smart contracts:
Public
This allows anyone to call and use this function
Private
This allows only the current contract and its functions to call it directly.
Internal
This is similar to private except it also allows derived contracts to use its functionality
External
External can only be called externally by other contracts unless the "this" keyword is used with the function call.
The second keyword in the constructor definition "payable" you may not be familiar with unless you have worked on blockchain projects. The word payable within solidity is needed on any item that can receive Ether. So, by setting the constructor as payable we can send a base amount of Ether to the contract when its deployed. This will add an initial monetary liquidity for whatever functionality the contract is providing. For example, if this were a gambling game, we would need some initial Ethereum to payout our winners before our revenues catch up with our payouts and we start collecting large sums of failed gambling revenue.
Within the constructor is an example of how comments are handled in solidity, the simple double forward slash is used like in most languages. Comments function in the same way as any other language in that they are not processed and they are ignored by the compiler but are useful for understanding the code you wrote later after you have taking time apart from reading your code.
Finally, we have our simple hello function starting on line 10. Again, there is a lot going on here. First is the name of the function with parentheses that can contain arguments like in any other language. However, this function does not take arguments.
You will notice two more keywords in the function definition "pure" and "returns". Returns is simply the way the function denotes that it will return a value to the user, which it then states directly after it what type of variable it returns. In this case, it returns a string in memory.We will talk about memory and storage later on and the security implications of them.
Next is the word "Pure" there are a couple types of functions in Solidity which will list below with a brief description.
View
This type of function does not modify or change the state of the contract but may return values and use global variables.
Pure
A pure function is a function which is completely self-contained in that it only uses local variables and it does not change the state of the smart contract.
Finally, in line 11 we return our string to the user who called the function. In the context of a user, this could be a physical user using an application or smart contract functionality or it could actually be another smart contract calling the function.
Now that we talked over in detail all the new concepts to solidity programs using a small example, lets compile and run this code on remix.ethereum.org.
Action Steps:
ü Browse to remix.etherum.org ü Type out the the code from above (Do not copy Paste it) ü Compile and deploy the code ü Review the transaction in the log window
Intro to the Remix Development Environment Video
In Remix create a new file and type out the example helloworld code. I would suggest that you actually type out all of the examples in this book. They will not be exhaustive or long and will provide you great value and make you comfortable when it comes to writing your own exploits and using the compilers and tools. These are all essential tools to your understanding.
Within your remix environment, you will want to select the compiler version 0.6.6 to ensure that this code runs correctly. If you typed out the code correctly you should not receive any errors and you will be able to deploy and interact with it. In the following video we will walk you through that process and explain some nuances of solidity.
Explaining and Compiling HelloWorld Video:
Lets now quickly review a few key points about the transaction that you saw within the video when compiling your code. This transaction is shown below.
The output above is a hello transaction which contains the relevant data retrieved when you executed the hello function in the video. The first important thing to notice is the word "CALL". In solidity there are call and send transactions. The difference between the two is whether they change the state of the blockchain or not. In this case we did not change the state, we only retrieved information so a CALL was issued.If we were changing variables and sending values then a SEND transaction would have been issued instead.
Next you will see the "From" address which should correspond with the address you used to call the transaction.The "To" field should be the address the smart contract was given when you deployed the smart contract. You can view this on your deployment screen next to the deployed contract name by hitting the copy button and pasting it somewhere to see the full value.
You will then see the costs and gas associated with the transaction. Costs change based on the size of the contracts and the assembly code created by the compiler. Each instruction has a cost. We will cover that later when we do a bit of debugging and decompiling.
Finally take note of the Decoded Output which contains the return string of "Hello World".
If you are new to solidity or new to programming in general this might have been a lot of information. In the next chapter we cover a few more key solidity concepts before moving on to exploiting vulnerabilities where a much more in depth understanding of how solidity works and its security implications will be explored. For more solidity resources and full-length free tutorials check out the following references
In this chapter we will take a look at bypassing incorrectly coded value transaction patterns within Ethereum smart contracts. These incorrectly coded patterns can lead to Reentrancy attacks that ultimately allow an attacker to liquidate the contract of all of its funds without much effort. The incorrect order of operations allows an attacker to avoid require statements which check if a user's balance is high enough to send a transaction. We can use this to bypass incorrect logic patterns and drain a contract of its funds.
Reentrancy attacks allow an attacker to create a loop between a target contract and a malicious attacker owned contract. Instead of a normal user making a request, the request comes from the attacker's contract which does not let the target contracts execution complete until the evil tasks intended by the attacker are complete. Usually this task will be draining the funds out of the contract bit by bit until all of the contracts funds are transferred to the attacker's contract.
The checks effects interactions pattern is a secure coding pattern within Solidity on Ethereum which prevents an attacker from re-entering a contract over and over. It does this by ensuring that balances are updated correctly before sending a transaction. It does this by:
üChecking that the requirements are met before continuing execution.
üUpdating balances and making changes before interacting with an external actor
üFinally, after the transaction is validated and the changes are made interactions are allowed with the external entity
The incorrectly coded pattern that usually creates a vulnerable smart contract is the common sense approach that first checks if a user's balance is large enough for the transaction, then sends the funds to the user. Once the transaction goes through, without error, the amount is subtracted from the user's balance.
The problem is that if a hacker's contract calls the target smart contract rather than a valid user calling the contract, the hacker's contract can run code in a loop. The hacker can call the same function in the target contract again without ever reaching the code that subtracts from the user's balance. This means that the initial balance check that passed the first time will pass again and again and again because it is at the same balance that passed the first time. You see where this is going right? The transaction will continue until the balance for the whole contract is empty, rather than just the users balance. Let's take a look at a simple example in order to understand how this works.
19.function getBalance() public view returns (uint){
20.return balances[msg.sender];
21. }
22.}
Simple Reentrancy Target Analysis Video:
There are three functions in the above contract, but the one we need to pay special attention to is the one that interacts with outside users. The withdraw function sends funds to the address of the user who called the withdraw function. This would be classified as an interaction and needs to follow our secure pattern.
The line breakdown of the withdraw function is as follows:
üLine 12: Checks that you are only withdrawing the amount you have in your account or sends back an error.
üLine 13: Sends your requested amount to the address the requested a withdrawal.
üLine 15: Deducts the amount withdrawn from the accounts total balance.
üLine 16. Simply returns your current balance.
Based on the above breakdown this function is following a:
Checks à Interaction à Effects
which violates the
Checks à Effects à Interactions
Because we interact with an external entity prior to updating the effects, the target contract is at risk for a call by a malicious contract that executes a loop with a malicious purpose.
Essentially what will happen is that the attacker will use his own malicious contract to call the withdraw function after adding a small value to his account. When the withdraw function is called the attackers contract will attempt to withdraw a smaller amount then the attacker has in his account which will pass the Checks portion of the pattern on line 12.
Next the target contract will attempt to interact with the attacker's contract by sending the valid withdrawn value from the contract. However, the attacker will have a fallback function that receives the sent value and calls the withdraw function again.
The second time calling the target contract will result in the exact same checks and interaction without ever updating the balance via the Effects portion. Over and Over and Over again.
The Effects portion will only be updated after the attacker's loop ends and the damage is done. Which means that the attacker has withdrawn funds many times over, but only subtracted that value a single time. Potentially draining all of the funds of the contract.
If we take a look at the following attacker's contract, we will see how the attacker creates this loop and we can analyze the order of operations that makes this possible.
The attacking code above is used by the attacker to siphon funds from a vulnerable contract. The main attack code in this contract is found on lines 22-24. This code creates a looping condition into the other contract by using a fallback function.
What is a fallback function?
A fallback function is a default function in a contract that is called when no other function is specified. So, in this instance when the contract receives funds and no other directions from the withdraw function, then the fallback function will execute on line 22. The fallback function will check that the target contract still contains a balance larger then what we are requesting which is defined on line 8 as "1 Ether".
If this check passes then our contract calls back into the withdraw function again at line 24. Which starts the whole process over and over again until the balance of the target contract is less than 1 ether. Let's take a look at a graphical representation of this to help understand what's going on.
The picture above shows the target contract and the attackers contract side by side. The attack function calls into the withdraw function initially. Then the fallback function is entered from the withdrawal transaction and returns right back to the beginning of the withdraw function from the fallback functions call back into the contract. This forms the loop between withdraw and fallback until the contract is below 1 ether.
That explains the main attack portion of the contract. The other parts of this attacking contract are just helping setup for the attack for example the interface code at line 1 simply creates an interface into the target contract via its function definitions. This interface is then set to the address of the target contract on line 7. With this interface you can now call the functions directly with the bankAddress interface using the function name as seen in the deposit function and attack function to call deposit and withdraw.
There is one other function we didn't mention which has nothing to do with the attack but helps us claim our funds after the contract is sent the ether from the attack. This function is on line 18 named retrieveStolenFunds. It simply takes the balance of "this" contract and transfers it to our personal address.
Let's try attacking the banking contract to see Reentrancy in action. Type out the code above for the target contract and understand what each piece of the contract does. Then type out the attacker's contract and try to piece together what each part of the attack does and what the sequence of execution will be.
Note: It's important that you type out this code and do not copy paste as it will help you in spotting issues in the future and your understanding of how things work.
Action Steps:
üWith account 1 deploy the target simpleReentrancy contract
üDeposit 20 Ether into the account by adjusting the Value field and selecting Ether
üCopy paste the address of the target contract and enter it into the target Interface variable in the attackers contract
üDeploy the attacker's contract simpleReentrancyAttack contract
üDeposit 2 ether into your account using the attackers contract deposit function
üThen execute the attack function with the attack button
üWhy did it pause?
üWhen attack completes execution note your second accounts balance and click retrieveStolenFunds
üNote your new balance
After running the attack, you should have noticed that your balance was updated by roughly 22 ether give or take fees. This would be the balance of the target contract initially and your own balance returned. You would have also noticed a pause when you clicked attack. This is because you are waiting for the contracts loop to complete its execution. It was calling the contract over and over again until 22 times.
Exploiting Reentrancy on the Target Smart Contract:
Reentrancy is a relatively easy vulnerability to fix, yet also a very easy mistake to make. It's easy to make a mistake because the vulnerable logic makes sense in real world logic. The vulnerable code should function correctly, if it were not interacting with a malicious contract. However, we do not expect an attacker's contract to be the receiver of the withdraw, thus throwing a wrench in real world logic. This is why we need to re-code this to function correctly using a secure pattern when dealing with DApps and web3.0.
Now let's correct the coding pattern by switching the order of operations to first decrease the accounts balance and then complete then initiate the withdraw transaction. The following image shows both the vulnerable and fixed code, where the original code is the on top and the fixed code is below:
Action Steps:
üImplement these changes in your contract.
üRedeploy both contracts making sure to update the address of the target contract in the attacker's contract
üTry this attack again, following the steps from above and observe how the results vary
With this simple change, our contracts balance is not decreased with each call to the withdraw function only the attackers balance is reduced until the attacker runs out of funds. If the attacker were to keep calling this function, the require check at the beginning of the function would fail as soon as the attacker ran out of funds. However, due to the usage of Call.Value and the lack of error handling, the funds may be incorrectly handled in the contract and error checking must be manually implemented. This is what we will look at next in regards to low level vs high level transfer functions.
Another relevant topic is that of the ways to transfer funds within Solidity. The "call" which was used in the withdraw function is a low-level function which can lead to issues and is largely replaced by the usage of Send or Transfer. Let's break these out and explain them:
Call.value()()
üReturns false on failure
üForwards available gas
üLow level function
Call.Value is dangerous because it forwards all of the available gas allowing for a reentrancy attack. It also does not return an error message and requires you to parse out the return Boolean value and perform an action based on this check. For example, if you were to make changes in the effects prior to the call.value, you may need to manually revert these changes as part of your error checking actions.
Send()
üReturns false on failure
üForwards a gas value of 2300
üLow level function
The send function limits the gas value to 2300 which helps prevent a reentrancy as there is a limit to how much the function can actually do before it fails. However, this is also a low-level function and you must be mindful of the lack of errors when this does fail exactly like the Call.value.
Transfer()
üActually, throws an error on failure
üForwards a gas value of 2300
üHigh level function
The transfer function provides a gas limit like the Send function but additionally provides an error and will revert changes made to the user's balance.
All of these functions are available for sending value out of the contract, however, only use low level functions with caution, and make sure to do error checking and make decisions on those errors. This will prevent hidden bugs in your code from error conditions. Also make sure to properly follow the checks, effects, interactions pattern in your code.
The DAO attack was the most famous blockchain attack ever performed. The DAO was a venture capital fund which pooled investors Ether for funding projects much like a crowdfunding application. The project initially raised 12.7 million Ether which at the time was equal to about 150 million dollars.
This Smart Contract contained a SplitDao function meant for removing funds into a child DAO when a user didn't like a majority decision of how to use funds. However, a Reentrancy vulnerability within the split function was found that ultimately allowed the attacker to remove 3.6 million Ether from the contract. This was a lot of money, but the bigger issue was the decision made by the Ethereum community to roll back the transaction, and give the users their funds back. As this violates the immutability of the blockchain. This should never happen again, but due to the immaturity of the network at the time, they felt it was needed.
This is the only time the Ethereum network violated the immutability of the blockchain and rolled back transactions on the Ethereum blockchain. The decision created a major idealistic split in the Ethereum community resulting in a hard fork of the network. Because of this split we now Ethereum classic and Ethereum. The network hard forked into two separate chains. One that contains the loss of funds on Ethereum Classic and one chain that does not contain the rollback, which is what we know as Ethereum.
Below we can see a snipped version of the original SplitDAO function which contained the issue:
If you take a look at lines 7-11 you will see a violation of our Checks à Effects à Interactions pattern.
On line 7-8 the contract is making withdrawal calls. However, following these withdrawals, the balances are updated on lines 10-11. If the attacker were to call back into the splitDao function when the interaction happened on line 8 then the attacker is able to drain the contract of millions of dollars. The balances are never updated until the attackers code is finished with its functionality.
In this chapter we took a look at secure coding patterns and high vs low level functions. We then interacted with vulnerable smart contracts that violated these secure coding principals. We exploited and fixed these issues ourselves in order to show how simple mistakes lead to huge losses in the case of attacks such as the famous DAO attack.