Top 10 Historical Smart Contract Vulnerabilities on Ethereum
INTRODUCTION TO BLOCKCHAIN AND THE SMART CONTRACTS
The emerging Blockchain techniques supports decentralized computing paradigm and it’s going mainstream. While it’s been thought primarily as the basis of Bitcoin(world most recognized Cryptocurrency),its application has gone beyond cryptocurrencies due to the introduction of smart contracts. Smart contracts is a computerized transaction protocol that executes the terms of a contract. They are self-enforcing pieces of software, which reside and run over a hosting blockchain. However writing trustworthy and safe contracts can be very challenging because of the complicated logic of the underlying high level language and its testability. There have been high profile incidents that indicate blockchain smart contracts contains various code-security vulnerabilities, causing loss of funds. Developers should first understand these vulnerabilities before widespread implementation of smart contracts. Security is a very serious issue in the case of smart contracts. Recent hacks of smart contracts raised the issue of the vulnerable of some smart contracts,the main popular and widely used with the highest community is the etherum smart contract which has held digital assets worth over $22billion as of November 2018 but unfortunately,there assets are not always secure as we wish,as a multitude of hacks and bugs. Hacks somehow makes institutional investors and regulators wary of the blockchain technology..
let’s look at the Top 10 historical Etherum smart contracts that were widely known and used before any vulnerabilities were discovered, the value lost and the creativity of the exploit.
1) THE DAO
At the beginning of May 2016, a few members of the Ethereum community announced the inception of The DAO, which was also known as Genesis DAO. It was built as a smart contract on the Ethereum blockchain. The coding framework was developed open source by the Slock. It team but it was deployed under “The DAO” name by members of the Ethereum community. The DAO had a creation period during which anyone was allowed to send Ether to a unique wallet address in exchange for DAO tokens on a 1–100 scale. The creation period was an unexpected success as it managed to gather 12.7M Ether (worth around $150M at the time), making it the biggest crowdfund ever. At some point, when Ether was trading at $20, the total Ether from The DAO was worth over $250 million.
In essence, the platform would allow anyone with a project to pitch their idea to the community and potentially receive funding from The DAO. Anyone with DAO tokens could vote on plans, and would then receive rewards if the projects turned a profit. With the financing in place, things were looking up.
On June 17,2016, a month after ICO, the DAO was attacked a mo by unknown hackers,exploiting a combination of vulnerabilities in the DAO smart contract and 3.5M ETH Which was valued around $50M at the time of the attack.
The attack happened due to a RECURSIVE CALLING VULNERABILITY, an external “call” present in the code path that should have been possible to execute only once per address, was positioned before the part of the code that revoked the address right to trigger the code path after first execution. This allowed the hacker to use the external call to execute the code path recursively multiple times,thereby draining much more funds from the contract than should have been possible.
What exactly happened?
This exploit in the DAO is clearly not trivial; the exact programming pattern that made the DAO vulnerable was not only known but fixed by the DAO creators themselves in an earlier intended update to the framework’s code. Ironically, as they were writing their blog posts and claiming victory, the hacker was preparing and deploying an exploit that targeted the same function they had just fixed to drain the DAO of all its funds.
Let’s get into the overview of the attack. The attacker was analyzing DAO.sol, and noticed that the ‘splitDAO’ function was vulnerable to the recursive send pattern we’ve described above: this function updates user balances and totals at the end, so if we can get any of the function calls before this happens to call splitDAO again, we get the infinite recursion that can be used to move as many funds as we want (code comments are marked with XXXXX, you may have to scroll to see them):
function splitDAO(
uint _proposalID,
address _newCurator
) noEther onlyTokenholders returns (bool _success) { ...
// XXXXX Move ether and assign new Tokens. Notice how this is done first!
uint fundsToBeMoved =
(balances[msg.sender] * p.splitData[0].splitBalance) /
p.splitData[0].totalSupply;
if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false) // XXXXX This is the line the attacker wants to run more than once
throw; ...
// Burn DAO Tokens
Transfer(msg.sender, 0, balances[msg.sender]);
withdrawRewardFor(msg.sender); // be nice, and get his rewards
// XXXXX Notice the preceding line is critically before the next few
totalSupply -= balances[msg.sender]; // XXXXX AND THIS IS DONE LAST
balances[msg.sender] = 0; // XXXXX AND THIS IS DONE LAST TOO
paidOut[msg.sender] = 0;
return true;
}
The basic idea is this: propose a split. Execute the split. When the DAO goes to withdraw your reward, call the function to execute a split before that withdrawal finishes. The function will start running without updating your balance, and the line we marked above as “the attacker wants to run more than once” will run more than once. What does that do? Well, the source code is in TokenCreation.sol, and it transfers tokens from the parent DAO to the child DAO. Basically the attacker is using this to transfer more tokens than they should be able to into their child DAO.
How does the DAO decide how many tokens to move? Using the balances array of course :
uint fundsToBeMoved = (balances[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply;
Because p.splitData[0]
is going to be the same every time the attacker calls this function (it’s a property of the proposal p, not the general state of the DAO), and because the attacker can call this function from withdrawRewardFor before the balances array is updated, the attacker can get this code to run arbitrarily many times using the described attack, with fundsToBeMoved
coming out to the same value each time.
The first thing the attacker needed to do to pave the way for his successful exploit was to have the withdraw function for the DAO, which was vulnerable to the critical recursive send exploit, actually run. Let’s look at what’s required to make that happen in code (from DAO.sol):
function withdrawRewardFor(address _account) noEther internal returns (bool _success) {
if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account])
throw;uint reward =
(balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account];
if (!rewardAccount.payOut(_account, reward)) // XXXXX vulnerable
throw;
paidOut[_account] += reward;
return true;
}
If the hacker could get the first if statement to evaluate to false, the statement marked vulnerable would run. When that statements runs, code that looks like this would be called:
function payOut(address _recipient, uint _amount) returns (bool) {
if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner))
throw;
if (_recipient.call.value(_amount)()) { // XXXXX vulnerable
PayOut(_recipient, _amount);
return true;
} else {
return false;
}
Notice how the marked line is exactly the vulnerable code mentioned in the description of the exploit we linked!
That line would then send a message from the DAO’s contract to _recipient
(the attacker). _recipient
would of course contain a default function, that would call splitDAO again with the same parameters as the initial call from the attacker. Remember that because this is all happening from inside withdrawFor
from inside splitDAO, the code updating the balances in splitDAO hasn’t run. So the split will send more tokens to the child DAO, and then ask for the reward to be withdrawn again. Which will try to send tokens to _recipient
again, which would again call split DAO before updating the balances array.
Let’s take a look at the DAO’s reward address. The DAO accounting documentation from Slockit pegs this address as 0xd2e16a20dd7b1ae54fb0312209784478d069c7b0. Check that account’s transactions and you see a pattern: 200 pages of .00000002 ETH transactions to 0xf835a0247b0063c04ef22006ebe57c5f11977cc4 and 0xc0ee9db1a9e07ca63e4ff0d5fb6f86bf68d47b89, the attacker’s two malicious contracts
Executing the Split
Having painstakingly described all the boring technical aspects of this attack. Let’s get to the fun part, the action: executing the malicious split. The account that executed the transactions behind the split is 0xf35e2cc8e6523d683ed44870f5b7cc785051a77d. The child DAO they sent funds to is 0x304a554a310c7e546dfe434669c62820b7d83490. The proposal was created and initiated by account 0xb656b2a9c3b2416437a811e07466ca712f5a5b5a (you can see the call to createProposal in the blockchain history there). Deconstructing the constructor arguments that created that child DAO leads us to a curator at 0xda4a4626d3e16e094de3225a751aab7128e96526. That smart contract is just a regular multi signature wallet, with most of its past transactions being adding/removing owners and other wallet management tasks. Nothing interesting there.
Extending the split
This step is an update to the original update, and covers how the attacker was able to turn a ~30X amplification attack (due to the max size of Ethereum’s stack being capped at 128) to a virtually infinite draining account.
Savvy readers of the above may notice that, even after overwhelming the stack and executing many more malicious splits than was required, the hacker would have their balance zeroed out by the code at the end of splitDAO
:
function splitDAO(
....
withdrawRewardFor(msg.sender); // be nice, and get his rewards
totalSupply -= balances[msg.sender];
balances[msg.sender] = 0;
paidOut[msg.sender] = 0;
return true;
}
So how did the attacker get around this? Thanks to the ability to transfer DAO tokens, he didn’t really need to! All he had to do was call the DAO’s helpful transfer function at the top of his stack, from his malicious function:
function transfer(address _to, uint256 _amount) noEther returns (bool success) {
if (balances[msg.sender] >= _amount && _amount > 0) {
balances[msg.sender] -= _amount;
balances[_to] += _amount;
...
By transferring the tokens to a proxy account, the original account would be zeroed out correctly at the end of splitDAO (notice how if A transfers all its money to B, A’s account is already zeroed out by transfer before it can be zeroed out by splitDAO). The attacker can then send the money back from the proxy account to the original account and start the whole process again. Even the update to totalSupply in splitDAO is missed, since p.totalSupply[0]
is used to calculate the payout, which is a property of the original proposal and only instantiated once before the attack occurs. So the attack size stays constant despite less available ETH in the DAO with every iteration.
The evidence of two malicious contracts calling into withdrawRewardFor
on the blockchain suggests that the attacker’s proxy account was also an attack-enabled contract that simply alternated as the attacker with the original contract. This optimization saves the attacker one transaction per attack cycle, but otherwise appears unnecessary. Additional information on how the attacker successfully exploited The DAO smart contract and stole funds can be found in Phil Daian analysis of DAO exploit
How the attack would have been avoided
The biggest problem with The DAO was that it was vulnerable to re-entrancy. This allowed the attacker to execute malicious code in between the contract’s execution, and steal funds. A good code pattern that prevents that problem is doing pull payments instead of push payments.
This has been discussed by the Ethereum developer community for a while, so I decided to write a helper contract anyone can use to adopt this pattern easily. Just make your contracts inherit from this contract and replace send calls with asyncSend
:
/*
* PullPayment
* Base contract supporting async send for pull payments.
* Inherit from this contract and use asyncSend instead of send.
*/
contract PullPayment {
mapping(address => uint) public payments;
// store sent amount as credit to be pulled, called by payer
function asyncSend(address dest, uint amount) internal {
payments[dest] += amount;
}
// withdraw accumulated balance, called by payee
function withdrawPayments() external {
uint payment = payments[msg.sender];
payments[msg.sender] = 0;
if (!msg.sender.send(payment)) {
payments[msg.sender] = payment;
}
}
}
Every time you call asyncSend
, balance is added to the caller’s credit. Note that the function is internal and can only be called by this contract or contracts inheriting from it. Once someone has balance, they can call the withdrawPayments
function to get the actual payment sent.
This decoupling of payment from the rest of the contract logic isolates the send
call in such a way that each payee can’t mess with the rest of the funds or code. If you want to see an example usage of this class, check out this sample bid contract.
However, due to the nature of the Child-DAO’s, the funds were locked for 28 days, giving the community the chance to hard fork the ethereum chain to refund the investors. This move sparked a highly controversial debate in the community. After this ideological earthquake, a lot of investors argued that the immutability of the blockchain as sacrosanct and decided to still mine on the old chain resulting in the continuation of the legacy chain as Ethereum Classic. Afterwards, the DAO was liquidated and investors were refunded on the Ethereum chain.
2) PARITY