How I Snatched 153,037 ETH After A Bad Tinder Date
Over the weekend, I did a lot of swiping right. I’ve never had a Tinder go this fast from match to agreeing on a date time this fast before. I was thrilled! On the following Friday we went out. The evening started nice, but he got creepier and creepier by the hour. A few hours in, he’s a full-on creep. I had to bolt. Not a big deal — I prefer a night tracing scam ICO’s transactions anyway.
Okay, so, in the day I sit and watch streams of Bitcoin and Ethereum transactions flowing from markets escrow accounts to sellers, from exchanges to users, from users to mixing party contracts, and so on and so forth, flagging interesting transactions, following flows between accounts suspected of activities such as child pornography exchange, human trafficking, etc. 8 hours a day everyday. At night, when I’m not peer-pressured into going to a bar, I do the same thing but for my own fun. These days I track ICOs that are obviously scams and see how they move their money around. It can be quite fun, but that’s a post for another day.
Alright. Friday night. Noise from Bastille Day’s fireworks is filling the skies. I’m following a transaction trail I’ve worked on for a couple of weeks. One of the bigger transactions originated from a multi-sig wallet smart contract. I don’t know why I did that, but I clicked in the Etherscan link and skimmed through the code. I remember finding it funny how Gav/Nicolas wrote that assembly piece there to call an internal method (initWallet) in a the wallet library. Solidity is a shit language, so I wasn’t all that surprised. But, I mean, really? Wasn’t it so that you can call internal methods normally from derived contracts? I dunno, I’m not a Solidity programmer.
So I wanted to see how much of a moron either Gav/Nicolas or yours truly are, and decided to try it myself. I created wallet’s contract in my privatenet and referenced in another contract and tried to call initWallet(). No go. So I thought to myself “Hmm.. what if I make it an external method?”. So I scrolled to it and tried to remove the internal modifier. But wait! There’s no internal modifier in the first place. 😲
Okay okay. Deep breath. What does this mean? Well, young man, this means whomever has money in such wallet is possibly ducked! To be sure, I tried calling initWallet() externally with an arbitrary owner address. Sure enough, the wallet added me as an owner. DUCKED, I SAY!
At this stage, I wasn’t really sure what to make of it. Even with my non-existent sense of morality, I didn’t feel like cleaning the 253 ETH in the wallet I first found because it was too low to bother. Now, I’m not a rich guy, so 253 ETH is a nice amount of money, which is about half a year salary, but I don’t want burn this find on 253 ETH, plus I don’t know this guy so I left it alone.
Obviously, if I can get enough money for a nice early 20s retirement out of this, I’m not gonna say no. I mean, would you? I mentioned I’m not rich, but I’m not poor either, but this is very lucrative. I can finally pay off my mom and dad’s mortgage, buy my older brother a car, pay off my sister’s college lo… STOP! You’re daydreaming again! Now I need to find other vulnerable wallets with a lot of money in them.
The plan is straightforward:
1- Find the Archetypes: Those are the “root” contracts based on which wallets are created.
2- Find the Children: Once I have those archetype addresses, I can find all the affected wallets that use the archetypes as library.
3- Triage: Rank the wallets by amount and grab the money from a random sample. I was thinking about 10–11k ETH in total.
4- Snatch: Do it all in one go.
5- Clean: Spend the next couple of months cleaning it up. It’s my job to catch people like me, so I know how get away with cashing out that amount in dollars/euros.
#1 Find the Archetypes
Have I said I’m an Ethereum-moron? Because I am. I genuinely tried to grep the blockchain files on-disk. 😳
How can I identify the archetypes? Well, after reading up on Ethereum, I learned that method signatures are calculated by the EVM as:
get_first_4_bytes(keccak256(“method_name(arg1_type, …, argn_type)”))
In hex, that would be 0xe46dcfeb. Not a lot of entropy there, but probably unique enough for scavenging through the blockchain. I quickly whipped out the following code and loaded it in my Geth, and then went to get some sleep. By now, it’s around 04:00 the next day — I needed the sleep.
Next day I woke up to this:
Not bad. Now to step #2.
#2 Find the Children
My search method is very slow, but it worked. Searching through contract creation transactions is good enough for my purpose. Now I simply replaced the previous method signature with the newly-found archetype addresses. This was pretty straight forward.
I had some minor issues with Geth, but I didn’t have time since I had a work trip on Monday, so my retirement will have to wait.
On Monday night, once I was back from my trip, I loaded a backed up state from May and started a sync. This was pretty fast. I loaded my code in Geth and let it run all night. I woke up in the morning to a nice list of wallets and balances.
I plugged the list in Excel and ordered by balance. Ok, that’s A LOT of ETH. I got greedy. Why take 10k when I can empty one wallet and get ~ 25k? Ok. The limit is 25k. Let’s take this one. 0x91efffb9c6cd3a66474688d0a48aa6ecfe515aa5.
This was quite easy after I learned the API. It’s along the lines of
I snatched 26,793 ETH and was very happy with it. I bought with Bitcoin real quick and then went to bed. I slept like I’ve never slept before.
Next day while at work, the list of wallets and balances kept itching in the back of my head. Here I had access to several tens of times what I snatched already, so I wanted a bit more. At lunch time, I went home and emptied two more wallets.
This is the most annoying part. Getting even half of that amount in clean fiat is going to be a very difficult challenge. Getting it straight in cash is super easy, but getting to a stage where I can use it for the aforementioned purposes (paying loans, buying a house, etc.) is probably gonna take me until the end of next year.
- But Mitch, isn’t this wrong? No.
- Will you send me some money? No. Go find your own vuln and exploit it.
- Are you really gonna retire? Of course not! I actually do like my job.
- But Mitch, isn’t this unethical? I don’t know, maybe? Ethics isn’t a singleton that is applicable to all aspects of life. My work ethics are separate from my steal-from-rich-useless-ICOs ethics and those are separate from my family ethics.
- But Mitch, you took it from actual real people.. Look, here’s the thing. If you’re holding 30 million dollars in 250 lines of code that you haven’t audited, then it’s on you. Seriously. It takes any half-decent appsec guy less than one man-day to fleece those 250 lines. At most, that would cost them a few thousands of dollars. They didn’t do it because they wanted it all for free. They didn’t do it because they’re greedy and cheap. They absolutely deserve this.
- Why didn’t you take more? Fewer people going after you is always worth it. Plus nobody wants a fork or a price crash.
- How do we know it’s really you? It’s absolutely the least of my concerns what you believe or don’t believe.
Ropsten Tip Jar: 0x7BB4be527a4f3719e5d66a5f5Ee5f83c8d7e3aF1
Update 2017/9/14: Thanks for all nice comments. I’m glad that you guys found this entertaining and had fun reading it.
I’m also glad that a lot of you have picked up on the little things I’ve put here and there all over this piece. In case it wasn’t clear at this point, I’ll spell it explicitly — this is a fictional piece based on real events that happened on July 19th. This is my fan-fic take on how the Parity Hacker might have thought about how this went down.