I was introduced to a strange looking “honeypot” contract on Etherscan recently.
by Caleb Lau
I was introduced to a strange looking contract on Etherscan recently, which was reported that the owner had set it up to steal funds from unsuspecting participants (which in this case, are people who are trying to exploit the contract themselves). Systems set up as such are known as “honeypots”, set up with the intention to attract attackers, however is monitored and has the intention of diverting or exploiting the original attackers, which in this case results in the attackers losing funds.
The contract in question looks similar to the one below on address 0xE668750B9E902b408d228Ffe41abdb76c9d04cfC:
If we inspect the code, we can see the contract is a naively written contract which acts as a wallet protected with a password. A person could quite easily deploy this contract with any arbitrary password, and should there be the event where the owner no longer has access to the original private keys, he/she could still enter the correct password, match it with the hashedpassword as when set up, and retrieve all funds.
The assumption the owner seemingly missed is that people will not be able to see the password as it is hashed and the variables are not set to public, basically “security through obscurity”. Now, everything on the Ethereum network is public, and we could of course retrieve the password when it is sent to set up the wallet, as indicated by the transaction below:
The password in hex is 7468697369736d7970617373776f726400000000000000000000000000000000 (padded to 32 bytes), which is simply:
Awesome! It does look like we could exploit the contract and attempt to drain its balance. We will need to send 1 ETH with our transaction, but we will be getting it back as indicated by the code below (right, right??):
The unfortunate attacker then sends the transaction….
And…. Gets nothing back. And a couple minutes later two internal transaction appears, draining all ETH from the contract, leaving the attacker 1 ETH poorer.
So what really happened here? This contract is intentionally engineered with the below properties:
- Liberal use of “if” statements so transactions do look as though they have successfully executed, when in reality does not fulfill the criteria required. On the other hand require() would throw a revert and it would be clear as day the password is wrong. This allows the contract to look a lot more innocent, potentially tricking multiple attackers.
- Written naively to trick attackers into thinking it is badly written code by a new developer who does not know what he or she is doing.
- And this is the important bit – Etherscan presently do not show cross-contract calls which sends 0 value. So contracts could execute state changes, but as long as no value is sent, it will not show up on the transaction list. Etherchain on the other hand would list these calls.
Contract 0xA2D98d20eB3496ABb3Ab59D8E90AD069FCF30b59 is the culprit, originally set up to execute a call per point 3 above. This is done through this transaction:
If we load up the Parity trace for this transaction, we can very quickly see this transaction in fact does two calls, the first headed to the contract 0xA2D9…b59, resulting in an internal call which calls our wallet contract on 0xE668…cfC:
Notice the block number being 4516435, which is before transaction 0xce6488…….804a94 done on block 4516438!
And as setup checks if hashedpassword has already been initialised, the password would NOT be “thisismypassword” but whatever password which the attacker has already preemptively set previously.
And to confirm this, let’s check the storage:
The hashedpassword is not keccak256(“thisismypassword”), neither is the owner 0xd77e…ea8 per transaction 0xce6488…….804a94!
In this case this is how the exploit contract looks like. Note that executeExploit is passes 1 ETH and hence appearing on the internal tx list, however could be made similar to the above as it is the true owner anyhow, which obscures the attack sequence slightly more.
And if you’re interested, there’s more to read: