The phenomenon of smart contract honeypots
Hardly a week passes without large scale hacks in the crypto world. It’s not just centralised exchanges that are targets of attackers. Successful hacks such as the DAO, Parity1 and Parity2 have shown that vulnerabilities in smart contracts can lead to losing digital assets worth millions of dollars. Attackers are driven by making profits and with the incredible value appreciation in 2017 in the crypto world, individuals and organisations who hold or manage digital assets are often vulnerable to attacks. Especially smart contracts have become a prime target for attackers for the following reasons:
- Finality of transactions: This is a special property of blockchain systems and it means that once a transaction (or state change) took place it can’t be taken back or at least not with grave consequences which in case of the DAO hack led to a hard fork. For an attacker targeting smart contracts, finality is a great property since a successful attack can not easily be undone. In traditional banking systems this is quite different, an attack even though initially successful could be stopped and any transactions could be rolled back if noticed early enough.
- Monetising successful attacks is straight forward: Once the funds of a smart contract can be withdrawn to an attacker’s account, transferring the funds to an exchange and cashing out in Fiat while concealing ones identity is something that the attackers can get away with if they are careful enough.
- Availability of contract source code / byte code: Ethereum is a public blockchain and so at least the byte code of a smart contract is available to anyone. Blockchain explorers such Etherscan allow also to attach source code to a smart contract and so giving access to high level Solidity code to potential attackers.
Since we have established now why attackers find smart contracts attractive targets, let’s further look into the circumstances that could decide if a smart contracts gets attacked:
- Balance: The greater the balance of a smart contract the more attackers will try to attack it and the more time they are willing to spend to find a vulnerability. This is an easier economic equation than for none smart contract targets since the balance that can be potentially stolen is public and attackers have certainty on how profitable a successful attack could be.
- Difficulty/Time: This is the unknown variable in the equation. Yet the approach to look for potential targets can be automated by using smart contract vulnerability scanners. Availability of source code addtionally decreases analyis time while also lowering the bar for potential attackers to hack smart contracts since byte code is harder to read and therefore it takes more skill and time to analyse.
Taking the two factors above in consideration, one could assume that every smart contract published to the main net with a sufficient balance is analysed automatically by scanners or/and manually by humans for vulnerabilities and is likely going to be exploited if it is in fact vulnerable. The economic incentives and the availability of smart contracts on the public chain have given rise to a very active group attackers, trying to steal from vulnerable smart contracts. Among this larger group of attackers, a few seem to have specialised to hack the hackers by creating seemingly vulnerable smart contracts. In many ways these contracts have resemblance to honeypot systems. They are created to lure attackers with the following properties:
- Balance: Honeypots are created with an initial balance that often seem to be in the range of 0.5–1.0 ETH.
- Vulnerability: A weakness in the code that seemingly allows an attacker to withdraw all the funds.
- Recovery Mechanism: Allows owner to reclaim the funds including the funds of the attacker.
Let’s analyse three different types of smart contract honeypots that I have come across over the last couple of weeks.
honeypot1: MultiplicatorX3
The contract’s source code was published on Etherscan with a seemingly vulnerable function. Try to spot the trap.
Analyses (!Spoiler!):
This is a really a short contract and the multiplicate() function is the only function that does allow a call from anyone else than the owner of the contract. At first glance it looks like by transferring more than the current balance of the contract it is possible to withdraw the full balance. Both statements in line 29 and 31 try to reinforce the idea that this.balance is somehow credited after the function is finished. This is a trap since the this.balance is updated before the multiplicate() function is called and so if(msg.value>=this.balance) is never true unless this.balance is initially zero.
It seems that someone has actually tried to call multiplicate() with 1.1 Ether. Shortly after the owner has withdrawn the full balance.
honeypot2: Gift_1_ETH
The contract has a promising name, if you want to figure out the trap yourself have a look at the code here. Also check out the transaction log … why did 0xc4126a64c546677146FfB3f3D5A6F6d5A2F94DF1 lose 1 ETH?
Analyses (!Spoiler!):
It seems that 0xc4126a64c546677146FfB3f3D5A6F6d5A2F94DF1 did everything right. First SetPass() was called to overwrite hashPass and then GetGift() to withdraw the Ether. Also the attacker made sure PassHasBeenSet() has not been called. So what went wrong?
One important piece of information in order to understand honeypot2 is to clarify what internal transactions are. They actually do not exist according to the specifications in the Ethereum Yellow Paper (see Appendix A for terminologies). Transactions can only be sent by External Actors to other External Actors or non-empty associated EVM Code accounts or what is commonly referred to as smart contracts. If smart contracts exchange value between each other then they perform a Message Call not a Transaction. The terminology used by EtherScan and other blockchain explorers can be misleading.
It’s interesting how one takes information as a given truth if the data comes from a familiar source. In this case EtherScan does not show the full picture of what happened. The assumption is that the transaction (or message call) should show up in internal transactions tab but it seems that calls from other contracts that have msg.value set to zero are not listed currently. Etherchain on the other hand shows the transaction (or message call) that called PassHasBeenSet() with the correct hash and so denying any future password reset. The attacker (in this case more of a victim) could have also been more careful and actually read the contract storage with Mythril for instance. It would have been apparent that passHasBeenSet is already set to true.
honeypot3: TestToken
I have taken the trick from the honeypot contract WhaleGiveaway1 (see analysis) and combined it with one of my own ideas. The contract is available here on my Github. Something is missing here …
Analyses (!Spoiler!):
This contract relies on a very simple yet effective technique. It uses a lot of whitespaces to push some of the code to the right and out of the immediate visibility of the editor if horizontal scrolling is enabled (WhaleGiveaway1). When you try this locally in Remix and you purely rely on the scrolling technique like in WhaleGiveaway1 then the trick actually does not work. It would be effective if an attacker copies the code and is actually able to exploit the issue locally but then fails on the main net. This can be done using block numbers. Based on what network is used the block numbers vary significantly from the main net.
Ganache: starts from 0
Testrpc: starts from 1150000
Ropsten: a few weeks ago around 2596174
Main net: a few weeks ago around 5040270
Therefore the first if statement is only true on the main net and transfers all ETH to the owner. On the other networks the “invisible” code is not executed.
if (block.number > 5040270 ) {if (_owner == msg.sender ){_owner.transfer(this.balance);} else {throw;}}
EtherScan also had the horizontal scrolling enabled, but they deactivated it a few a few weeks ago.
TL;DR
Smart contract honeypot authors form a very interesting sub culture among a larger group of hackers trying to profit from vulnerable smart contracts. In general I would like to give anyone the following advice:
- Be careful where you send your ETH, it could be a trap.
- Be nice and don’t steal from people.
I have created a Github repo for honeypot smart contracts here. Should you have any honey pot contracts yourself that you want to share please feel free to push them to the repo or share them in the comments.