Hacking smart contracts

Kimi Ylilammi
February 20, 2020

Automatic laser cannon - Hacking smart contracts for profit

As money kept in smart contracts continues to grow, the motivation to hack them for money becomes more and more appealing. Contracts and their interactions with other contracts also get more and more complex over time, with an increasing amount of bugs to exploit.

Hacking smart contract platforms is more lucrative for hackers than many other kinds of targets. The hacks can be converted into money very fast, as the assets are highly liquid and there is no one stopping the movement of the funds. Also as the platforms are open for everyone to view, a hacker can simulate the attacks on a private version of the chain before conducting the exact same attack on a live network.

The unique features of a blockchain enables us to make a machine that exploits contracts automatically with minimal exposure.

Adjusting lasers towards profits

To hack contracts, we first need to find interesting contracts as our targets. These contracts should contain something valuable, such as Ether or ERC-20 tokens. These can easily be scraped from ethereum node by scanning through ERC-20 contracts of interest and getting all the current balance of all accounts. Once we have done this, we simply filter out normal wallets out of the list, we can also filter out contracts that occur frequently (as they are more likely to be battle tested). Generally, contracts are considered safer the longer they have been on a live network with a significant amount of funds at risk.

We can construct a simple metric that determine in what order of priority we should start hacking each contract:

priority_to_attack = funds_held_in_contract / (contract_age + our_time_spent_hacking)

This will prioritize contracts holding large amounts of funds while making them less and less valuable as time passes as its more likely someone has looked at the contracts already. We also add a term for our time spent attacking, as this will then decrease the contracts priority over the time we attack is, as we are more and more confident we are not gonna find anything to exploit there.

Charging our lasers in order of priority

Once we have identified an individual contract to attack, we can start finding attack vectors against it. We attempt to hack the contract for a certain amount of time (eg. 60 secs), and if no attack is found, we adjust our cannon and start attacking next contract in our priority queue. We come back attacking this contract again once its priority has risen higher than the other contracts again, and we contribute 60 secs towards it again.

One can start finding vulnerabilities for the contract for example by using Mythril. Mythril is a tool used to finding common vulnerabilities in smart contracts. However, as this is an open source tool, this is already in use in multiple similar bots, such that there is not much benefit in running it. However, once the tool updates it might be beneficial to keep running it for some time. There is also a question of speed: when a new contract with new funds gets deployed on the chain, the fastest bot to figure out the attack vector can grab the profits.

A more interesting approach is to develop one’s own system to attack the contract using evolutionary algorithms and machine learning. We can attack the contract by generating a random solidity contract, compiling it, deploying it and then executing the contract. At the end of the transaction, if our wallets ether or ERC20 balance is bigger than the funds we sent to the contract, we are victorious and we can move to the second step in our pipeline.

Here's a simple contract that we can use to steal our victims Ether:

pragma solidity 0.5.15;

import "@openzeppelin/contracts/ownership/Ownable.sol";
contract Cannon is Ownable {
  function ShootLazer(address contractToAttack) public payable onlyOwner  {
        uint256 ourBalance = address(this).balance;
        //START OF PAYLOAD
        victim(contractToAttack).giveUsYourEther(parameters);
        //END OF PAYLOAD
        uint256 newBalance = address(this).balance;
        require(newBalance >= ourBalance, "Failed to profit!");
  }
}

We cannot deploy such a contract on the main net multiple times thought, as that would get expensive fast, and someone could figure out what we are trying to attempt to do and figure out the attack vector faster than us. To get around this problem, we fork the main nets chain to our private network with Ganache. We can then spam this network as much as we like and no one is spying on us.

A naive approach in coming up with attack vectors would be to randomly generate different kinds of contracts and trigger them, then if the contract does not fail, we are profitable and we can move to the next step. However, this does not work as the amount of different transaction combinations is prohibitively high; we need to be smart about what kind of attacks we attempt.

A good starting point for possible transactions to attempt are the transactions that have been used for the contract already (we can fetch these from the chain). These are probably somewhat valid transactions and we can place them one after another in our contract and execute them. The second step is to investigate if the transaction sent to the contract changes the state of the contract in any way. We can use these as an indicator on how good a transaction was: the louder the contract gets after we shoot transaction towards it, the better the transaction is. If the contract does not do anything after a transaction, the transaction is not as useful as one that does. This gives us again a priority order on the transactions that we should try. We can then use evolutionary methods and machine learning to improve our attack vector and be able to generate smarter and smarter attack combinations of transactions. This is left as an exercise for the user: how can we further increase the smartness of the machine to find attack vectors of an unknown contract that we do not know anything about?

Smart contracts can also interact with other contracts, these interactions make it a bit harder to come up with attack vectors, as possibilities grow again. However, once we think hard and figure out how we can utilize them smartly, we enable our contract to be even more powerful in finding vulnerabilities.

Recently two hacks that have been made that utilized multiple contracts at once. (Robbery of $650 000+ and an $300 000+ attack) . These vulnerabilities could have been found by anyone (with enough computing power or skills, or probably both).

Both these hacks were done by using a new Ethereum paradigm called "Flash loan". Flash loans allow users to get a very big loan without any kind of collateral. The trick is that it has to be paid back during the same transaction. This enables anyone to get rich for a very brief moment. The automatic attack system can always start by taking a flash loan and then try to figure out whether there is a use for it. It can be later optimized away if there is no use for it.

Attacking some contracts also might require you to hold some amount of specific ERC20 tokens, these can be figured out from the contract itself (if they have ERC20 dependency). We can get these funds from an exchange in the beginning of our attack (eg. Uniswap).

Firing the cannon

Once we have identified a working attack vector, we should optimize it for max profit. We can theorize that the max profit that we can reach from a contract is it's maximum balance (ETH+ERC20). However, we might be able to hack other contracts via this contract so the theoretical maximum profit can be even bigger than the target contracts balance.

Our automatic system might also send too much funds to the contract and we would like to get this sum as small as possible while getting as much as rewards from the contract at the same time. We also might make unnecessary transactions, so these can be cleaned out.

We can achieve these optimizations by manually inspecting the attack vector or just continuing to mutate the attack while optimizing for these criteria. We also shouldn't spend too much time here, because someone might have found the same attack vector already. We thus should Optimize the attack vector fast Test the transaction in the highest block height of the main chain (using private fork of it) Execute the attack. Once the system has gotten good enough, we can remove humans in the loop and allow the machine to execute transactions automatically. We can then sleep our night calmly, let the machine make money while doing nothing, and observe in the crypto media what has happened in the chain while we slept. All this while having no idea ourselves what the machine has been up to.

Defending against such attacks

The built automatic laser cannon can find transaction sequences to execute that are morally okay to be conducted. These can be simple bounties, ICO's, puzzles or arbitrages. However not all attacks will be like that and it becomes a question of morals which sequences of transactions are okay to be conducted and which are not. Black hats do not really care about morals, and all the money for grabs is okay for them.

It is interesting to note that the attacks the machine builds and executes are self contained. Anyone looking at the chain can identify the machines contracts and deploy them themselves and execute them, before the machine has time to do that. Miners or front runners can look for these and execute the transactions before our laser cannon is able to do that. We can obfuscate the contracts in a way that doing this trivially is not simple.

White hats can defend against these attacks by improving development tools for smart contracts. White hats can also run a similar machine on the main net and hack the contracts before a black hat is able to attack it. This becomes a difficult problem though, as anyone can publish contracts on chain, it can be hard to find the developers behind the contract to inform them that the contract is vulnerable. It's also already too late if a vulnerable contract is live with funds inside it. It needs to be removed immediately and there is no time to waste. Should a white hat hacker hack the contract as fast as possible? What if the hack causes other damage than can be extracted via the hack? Who is responsible? It's probably safer for a white hack to let the contract be and let black hats to hack it instead. Or hack the contract anonymously and then figure out later did the hack turn out to be a black hat hack or a white hat hack.

Questions? Reach out to @Qhuesten (Kimi Ylilammi) on Twitter.