I've been trying to pick this honeypot contract apart, but I haven't figured out exactly how it works.
overview
The premise is pretty simple, the contract is set up to look like a "new year's gift" where the ETH is retrievable if someone (probably the gift receipient) puts in the correct password.
However the contract author has made a fatal "flaw". If a certain field named passHasBeenSet
is false, anyone can send the contract >1 ETH and become the contract's owner! Once you're the owner, you can call the revoce
function to pull out all of the funds in the contract.
What's the problem? I found this contract suspect because it was 1) created by the creator of another honeypot contract described on reddit and 2) generally strange - why post the source to Etherscan? Why allow someone else to become the owner if they pay more into the contract? The contract is designed with if statements/doesn't throw errors and looks like it was written by a beginner.
However, I couldn't initially spot anything wrong with the contract. Additionally, it seemed like it only had two transactions, a contract creation and a call to setPass
. This should still leave passHasBeenSet
set to false.
pen testing
I figured I might as well see if the contract was a Honeypot or not. I knew I could design the "attack" contract so I couldn't lose, but I was still curious.
I put together a contract that would call setPass
and then revoce
. If the contract's balance decreased, it would revert. This construction meant I couldn't lose any money.
The honeypot contract (& attacking contract) worked as expected on Ropsten. Any balance in the contract was extracted and could then be withdrawn from then attacking contract. However, the attacking contract failed on the mainnet. The balance was successfully sent to the target contract, but the revoce
failed so the contract's revert failsafe was triggered. Debugging the transaction showed that passHasBeenSet
was true.
So, how did the "vulnerable" contract manage to set passHasBeenSet
? I must be missing something pretty obvious.
passHasBeenSet
was true, how did your contract's call toSetPass(
) succeed in the first place?