ETH History: Re-entering the first major hack on Ethereum

So I was talking to one of my crypto friends and he didn’t know the infamous story of the DAO. In that moment I realized lots of y’all are new here, so let’s take a trip back to 2016:

Kanye just dropped The Life of Pablo, your friends are sending Harambe memes in the groupchat, life is good.

Meanwhile, on the internet a group of nerds are circlejerking to an idea of a decentralized autonomous organization! Wow! No one is really sure what it means, but it’s never been done before, because before there wasn’t the global computer called Ethereum. The first major dao was fittingly called The DAO. For those of you that weren’t part of ETH back then, this thing was kind of a bid deal. According to the infallible Wikipedia: As of May 2016, The DAO had attracted nearly 14% of all ether tokens issued to date.[1]

There really was something in the air for this thing back then. The idea of a decentralized entity investing and managing money really was pretty sick, even though now in 2022 it’s starting to sound overdone. Anyways, after raising a fuckton of money back then (over $150M+), people started reee’ing about real security issues the DAO might have. Check out Peter’s post here, where he explains it better than I could:

But I’m gonna explain it anyways.

Ethereum has this thing called a fallback function, which means you can overload the code on a contract that runs when someone sends a contract ETH without calling a specific method. Basically think of it as changing the default behavior when a contract receives ETH.

What Peter found was there was a way to abuse this fallback function, if a withdraw function on a contract wasn’t written correctly (and back then, everything was shittier so you gotta assume none of it was written well).

Wanna see how to lose a $100m? Just use this snippet:

function withdrawBalance() {
  amountToWithdraw = userBalances[msg.sender];
  if (!(msg.sender.call.value(amountToWithdraw)())) { throw; }
  userBalances[msg.sender] = 0;
}

So msg.sender.call.value(amountToWithdraw) was the boomer way of sending ether to contracts and addresses back then. Nowadays kids got send() and transfer(). And the nice things about those two functions is they have default gas limits. Because call() didn’t, you could do something interesting unintended things.

Like say, for example, hijacking the withdrawBalance(), and recursing the first two lines until the contract was completely drained.

Yup. That’s what happened. The hacker of the DAO realized he could call withdrawBalance() within the fallback method (which is called in with the msg.sender.call.value function call) and create an infinite loop that would give his contract all the ether in the DAO.

Quite devilish.

It’s actually really simple, but crazy to think about. Thankfully today we those two new functions so we usually don’t have to call call(), and OpenZeppelin’s wonderful ReentrancyGuard to protect us from ourselves.

Smart contract development has evolved A LOT since then. It’s pretty fun looking at old code and seeing the kinds of stuff that got pushed to prod. Don’t get me wrong, I’m the kind of guy that’d fuck this up in 2022, but the ecosystem as a whole feels like it’s evolved and moved towards better paradigms in SC development.

Subscribe to 0xben
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.