I’ve recently saw a tweet about a sneaky scam where someone would leak a privatekey of an account that owns some token and needs gas to withdraw them. It had a smart contract that would transfer any eth send to its address to another contract. I thought it was pretty good and it reminded of another “scam” I’ve seen few months ago that in my opinion is even sneakier. The common point is that both of these scams are trying to get money from people who had bad intentions in the first place.
People already wrote about Ethereum honeypots but the one I’m going to present in this article is the sneakiest of them all. If you don’t know what is a honeypot, in the case of Ethereum it’s a smart contract that wants to make the person who reads the code think he can hack it and withdraw the ether from the contract. But in order to hack it you will have to send ether and you’ll never get them back. So it’s basically “hack the hackers”.
I’ve been monitoring the Ethereum chain for month and my eyes became pretty good at spotting honeypots. Usually they’re not so original. But in this case I had a serious doubt. I even asked a hacker friend whom I consider to be better than myself at solidity to have a look. His first reaction was to tell me that it’s not a honeypot and that you can withdraw the ether from that contract easily. So he proceeded and sent 1 eth to the contract and lost it… From that point I knew that honeypot was on another level =D.
This is the code of the contract:
Seems pretty simple and vulnerable right? The
shuffle() function will generate a “random” number and this function is called at first in the constructor.
It’s pretty much known by everyone at this point that
uint8(sha3(now, block.blockhash(block.number-1))) % 20 + 1;
is not a good way to generate a random number. This is our bait. If you’re a wannabe hacker and you see this line your heartbeat starts to pump as you open your ether wallet hoping to be the first to snatch the ether from this contract. To get the “secret” number you could simply query the storage of the contract since it was created in the constructor. But of course you could also predict every new “secret” number that
shuffle() would generate. We’re not going to go into this here as I think this was covered many times before.
So our wannabe hacker is now baited, he opened his wallet, got the number and called the
play() function with the right number and some ether aaaaand…
But what happened exactly?
In order to understand it I called the
play() function with the correct number and observed what happened in the Remix debugger. In this case our secret number is 17 (in hex 0x11). Since the
secretNumber variable is the first one declared in the contract it’s located at address 0:
As I progress in the function I can see that the value at that address got overwritten:
I added 2 events in the code to output what happens in a clear way:
In Solidity, structs default to
storage (the other option being
memory)and since our struct is uninitialised it actually points to address 0. So at line 37 of this contract we write to address 0 and since it’s the storage address of
secretNumber it changes it’s value. Therefore
secretNumber is never equal to your number and you don’t get the money… After that a new call to
shuffle() is made and everything looks fine again ¯\_(ツ)_/¯
Here’s the first occurence of this contract I’ve found back in february:
The Ethereum BlockChain Explorer, API and Analytics Platformetherscan.io
I also have a Github repo where I keep track of known honeypots: